web-dev-qa-db-ja.com

C#でいつデリゲートを使用しますか?

C#でのデリゲートの使用方法は何ですか?

100
Maxime Rouiller

C#にラムダ式と匿名メソッドが追加されたので、デリゲートをさらに使用します。 C#1では、ロジックを実装するために常に別のメソッドが必要でしたが、デリゲートを使用することはしばしば意味をなしませんでした。最近では、次の目的でデリゲートを使用しています。

  • イベントハンドラー(GUIなど)
  • 開始スレッド
  • コールバック(非同期APIなど)
  • LINQおよび類似(List.Findなど)
  • 私が効果的に「テンプレート」コードを内部に適用したい他の場所(デリゲートが特殊化を提供する場所)
98
Jon Skeet

デリゲートは多くの目的に非常に役立ちます。

そのような目的の1つは、データのシーケンスをフィルタリングするためにそれらを使用することです。この例では、1つの引数を受け入れ、デリゲート自体の実装に応じてtrueまたはfalseを返す述語デリゲートを使用します。

これはばかげた例です-これからもっと有用なものを推定できると確信しています:

using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<String> names = new List<String>
        {
            "Nicole Hare",
            "Michael Hare",
            "Joe Hare",
            "Sammy Hare",
            "George Washington",
        };

        // Here I am passing "inMyFamily" to the "Where" extension method
        // on my List<String>.  The C# compiler automatically creates 
        // a delegate instance for me.
        IEnumerable<String> myFamily = names.Where(inMyFamily);

        foreach (String name in myFamily)
            Console.WriteLine(name);
    }

    static Boolean inMyFamily(String name)
    {
        return name.EndsWith("Hare");
    }
}
27
Andrew Hare

別の興味深い答えを見つけました:

同僚からこの質問がありました。NETのデリゲートのポイントは何ですか?私の答えは非常に短く、彼がオンラインで見つけられなかったもの:メソッドの実行を遅らせることです。

ソース: LosTechies

LINQがやっているように。

13
Maxime Rouiller

デリゲートを使用して、関数型の変数とパラメーターを宣言できます。

「リソース借用」パターンを検討してください。リソースの作成とクリーンアップを制御し、その間にクライアントコードがリソースを「借用」できるようにします。

これはデリゲート型を宣言します。

public delegate void DataReaderUser( System.Data.IDataReader dataReader );

このシグネチャに一致する任意のメソッドを使用して、このタイプのデリゲートをインスタンス化できます。 C#2.0では、匿名メソッドを使用するだけでなく、メソッドの名前を使用するだけで、暗黙的にこれを行うことができます。

このメソッドは、タイプをパラメーターとして使用します。デリゲートの呼び出しに注意してください。

public class DataProvider
{
    protected string _connectionString;

    public DataProvider( string psConnectionString )
    {
        _connectionString = psConnectionString;
    }

    public void UseReader( string psSELECT, DataReaderUser readerUser )
    {
        using ( SqlConnection connection = new SqlConnection( _connectionString ) )
        try
        {
            SqlCommand command = new SqlCommand( psSELECT, connection );
            connection.Open();
            SqlDataReader reader = command.ExecuteReader();

            while ( reader.Read() )
                readerUser( reader );  // the delegate is invoked
        }
        catch ( System.Exception ex )
        {
            // handle exception
            throw ex;
        }
    }
}

この関数は、次のように匿名メソッドで呼び出すことができます。匿名メソッドは、それ自体の外部で宣言された変数を使用できることに注意してください。これは非常に便利です(ただし、この例は少し工夫されています)。

string sTableName = "test";
string sQuery = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='" + sTableName + "'";

DataProvider.UseReader( sQuery,
    delegate( System.Data.IDataReader reader )
    {
        Console.WriteLine( sTableName + "." + reader[0] );
    } );
12
harpo

多くの場合、デリゲートは1つのメソッドのインターフェイスの代わりに使用できます。これの一般的な例は、オブザーバーパターンです。他の言語では、何かが起こったという通知を受け取りたい場合、次のように定義できます:

class IObserver{ void Notify(...); }

C#では、これはイベントを使用してより一般的に表現されます。ハンドラーはデリゲートです。以下に例を示します。

myObject.SomeEvent += delegate{ Console.WriteLine("..."); };

関数に述語を渡す必要がある場合、たとえばリストからアイテムのセットを選択する場合など、デリゲートを使用するもう1つの優れた場所:

myList.Where(i => i > 10);

上記はラムダ構文の例であり、次のように記述することもできます。

myList.Where(delegate(int i){ return i > 10; });

デリゲートを使用すると便利なもう1つの場所は、ファクトリ関数を登録することです。次に例を示します。

myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);

これがお役に立てば幸いです!

10
jonnii

私はこれに本当に遅れて入りますが、今日の代表者の目的を理解するのに苦労し、彼らの目的をよく説明していると同じ出力を与える2つの簡単なプログラムを書きました。

NoDelegates.cs

using System;

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void checkInt(int a) {
        Console.Write("checkInt result of {0}: ", a);
        if (a < MAX_VALUE && a > MIN_VALUE)
            Console.WriteLine("max and min value is valid");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Test.checkInt(1);
        Test.checkMax(1);
        Test.checkMin(1);

        Test.checkInt(10);
        Test.checkMax(10);
        Test.checkMin(10);

        Test.checkInt(20);
        Test.checkMax(20);
        Test.checkMin(20);

        Test.checkInt(30);
        Test.checkMax(30);
        Test.checkMin(30);

        Test.checkInt(254);
        Test.checkMax(254);
        Test.checkMin(254);

        Test.checkInt(255);
        Test.checkMax(255);
        Test.checkMin(255);

        Test.checkInt(256);
        Test.checkMax(256);
        Test.checkMin(256);
    }
}

Delegates.cs

using System;

public delegate void Valid(int a);

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void checkInt(int a) {
        Console.Write("checkInt result of {0}: ", a);
        if (a < MAX_VALUE && a > MIN_VALUE)
            Console.WriteLine("max and min value is valid");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Valid v1 = new Valid(Test.checkInt);
        v1 += new Valid(Test.checkMax);
        v1 += new Valid(Test.checkMin);
        v1(1);
        v1(10);
        v1(20);
        v1(30);
        v1(254);
        v1(255);
        v1(256);
    }
}
10
will

デリゲートは、イベントを使用するたびに使用されます。これが、デリゲートが機能するメカニズムです。

さらに、デリゲートは、LINQクエリの使用などに非常に役立ちます。たとえば、多くのLINQクエリはデリゲート(多くの場合Func<T,TResult>)フィルタリングに使用できます。

5
Reed Copsey

少し異なる用途は、反射を高速化することです。つまり、毎回リフレクションを使用する代わりに、Delegate.CreateDelegate(型付き)メソッドへのデリゲート(MethodInfo)を作成し、代わりにそのデリゲートを呼び出します。これは、チェックがすでに行われているため、呼び出しごとにmuchより速くなります。

Expressionを使用すると、同じことを実行してコードをその場で作成することもできます。たとえば、実行時に選択した型の+演算子を表すExpressionを簡単に作成できます(演算子を提供するため)言語が提供しないジェネリックのサポート);そして、あなたはExpressionを型指定されたデリゲートにコンパイルすることができます-仕事は完了しました。

5
Marc Gravell

イベントハンドラーをイベントにサブスクライブする

4
Manu

例は here のようになります。特定の要件を満たすオブジェクトを処理する方法があります。ただし、オブジェクトを複数の方法で処理できるようにする必要があります。個別のメソッドを作成する代わりに、オブジェクトを処理する一致するメソッドをデリゲートに割り当て、オブジェクトを選択するメソッドにデリゲートを渡すだけで済みます。これにより、1つのセレクターメソッドに異なるメソッドを割り当てることができます。これを簡単に理解できるようにしました。

2
rookie1024

デリゲートを使用してスレッドと通信します。

たとえば、ファイルをダウンロードするwinフォームアプリがあるとします。アプリはワーカースレッドを起動してダウンロードを実行します(GUIがロックするのを防ぎます)。ワーカースレッドはデリゲートを使用してステータスメッセージ(ダウンロードの進行状況など)をメインプログラムに返送し、GUIがステータスバーを更新できるようにします。

1
Danny Frencham

デリゲートの使用

  1. イベント処理
  2. マルチキャスティング
0
Rajeshwaran S P

In Array.Sort(T []配列、比較比較)、List.Sort(比較比較)などの比較パラメーター

0
GregUzelac

デリゲートは、参照によってメソッドを呼び出すために使用されます。例えば:

  delegate void del_(int no1,int no2);
class Math
{
   public static void add(int x,int y)
   {
     Console.WriteLine(x+y);
   }
   public static void sub(int x,int y)
   {
     Console.WriteLine(x-y);
   }
}



    class Program
    {
        static void Main(string[] args)
        {
            del_ d1 = new del_(Math.add);
            d1(10, 20);
            del_ d2 = new del_(Math.sub);
            d2(20, 10);
            Console.ReadKey();
        }
    }
0
mahesh
  1. イベントハンドラー用

  2. メソッドパラメータでメソッドを渡すには

0

使用法の最初の行は、Observer/Observable(イベント)パターンを置き換えることです。 2つ目は、戦略パターンのニースエレガントバージョンです。これらの最初の2つよりも難解ですが、他のさまざまな使用法を収集できます。

0
x0n

イベント、その他の任意の操作

0
Jeffe

動作をカプセル化したいが、統一された方法で呼び出す場合。イベントハンドラ、コールバック関数など。インターフェイスとキャストを使用して同様のことを実現できますが、動作が必ずしもtypeまたはobjectに関連付けられているとは限りません。場合によっては、カプセル化する必要がある動作のみがあります。

0
Bob King

私の知る限り、デリゲートは関数ポインターに変換できます。これは、元のプログラマーがそれを実現するための準備をしていなくても、オブジェクトポインターを効果的にオブジェクト指向にすることができるため、関数ポインターを使用するネイティブコードと相互運用するときに非常に簡単になります。

0
Puppy

遅延パラメーターの初期化!以前のすべての回答(戦略パターン、オブザーバーパターンなど)に加えて、デリゲートを使用すると、パラメーターの遅延初期化を処理できます。たとえば、かなり時間がかかり、特定のDownloadedObjectを返す関数Download()があるとします。このオブジェクトは、特定の条件に応じてストレージによって消費されます。通常、次のことを行います。

storage.Store(conditions, Download(item))

ただし、デリゲート(より正確にはラムダ)を使用すると、条件とFunc <Item、DownloadedObject>を受け取り、次のように使用するようにストアの署名を変更することにより、次のことができます。

storage.Store(conditions, (item) => Download(item))

したがって、ストレージは必要に応じてデリゲートのみを評価し、条件に応じてダウンロードを実行します。

0