C# でデリゲートをどのように使用していますか?
20 に答える
C# でラムダ式と匿名メソッドを使用できるようになったので、デリゲートをより多く使用します。C# 1 では、ロジックを実装するために常に別のメソッドを用意する必要がありましたが、デリゲートを使用しても意味がないことがよくありました。最近では、次の目的でデリゲートを使用しています。
- イベント ハンドラー (GUI など)
- スレッドの開始
- コールバック (非同期 API など)
- LINQ など (List.Find など)
- 内部に特殊なロジックを含む「テンプレート」コードを効果的に適用したい他の場所 (デリゲートが特殊化を提供する場所)
デリゲートは、多くの目的で非常に役立ちます。
そのような目的の 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");
}
}
別の興味深い答えが見つかりました:
同僚が私にこの質問をしたところです - .NET でのデリゲートのポイントは何ですか? 私の答えは非常に短く、彼がオンラインで見つけられなかったものでした。メソッドの実行を遅らせることです。
出典:LosTechies
LINQ が行っているように。
デリゲートは、多くの場合、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);
これが役立つことを願っています!
デリゲートを使用して、関数型の変数とパラメーターを宣言できます。
例
「リソースの借用」パターンを考えてみましょう。リソースの作成とクリーンアップを制御し、その間にクライアント コードがリソースを「借用」できるようにしたい。
これはデリゲート型を宣言します。
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] );
} );
私はこれに非常に遅れて入ってきましたが、今日はデリゲートの目的を理解するのに苦労していました.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);
}
}
デリゲートは、イベントを使用するたびに使用されます。これが、イベントが機能するメカニズムです。
さらに、デリゲートは、LINQ クエリの使用などに非常に役立ちます。たとえば、多くの LINQ クエリは、Func<T,TResult>
フィルター処理に使用できるデリゲート (多くの場合 ) を取ります。
少し異なる用途は、リフレクションを高速化することです。つまり、毎回リフレクションを使用する代わりにDelegate.CreateDelegate
、メソッド (a MethodInfo
) への (型指定された) デリゲートを作成し、代わりにそのデリゲートを呼び出すことができます。チェックがすでに行われているため、これは呼び出しごとにはるかに高速です。
を使用Expression
すると、同じことを行ってその場でコードを作成することもできます。たとえば、Expression
実行時に選択された型の + 演算子を表す を簡単に作成できます (言語が提供しないジェネリックの演算子サポートを提供するため)。 ; 型付きのデリゲートにコンパイルできますExpression
-仕事は完了です。
イベントハンドラーをイベントにサブスクライブする
例はここに見られるようになります。特定の要件を満たすオブジェクトを処理するメソッドがあります。ただし、オブジェクトを複数の方法で処理できるようにする必要があります。個別のメソッドを作成する代わりに、オブジェクトを処理するマッチングメソッドをデリゲートに割り当て、オブジェクトを選択するメソッドにデリゲートを渡すことができます。このようにして、1つのセレクターメソッドに異なるメソッドを割り当てることができます。これをわかりやすくするように心がけました。
デリゲートを使用してスレッドと通信します。
たとえば、ファイルをダウンロードする win フォーム アプリがあるとします。アプリはワーカー スレッドを開始してダウンロードを実行します (これにより、GUI がロックされるのを防ぎます)。ワーカー スレッドはデリゲートを使用してステータス メッセージ (ダウンロードの進行状況など) をメイン プログラムに送り返し、GUI がステータス バーを更新できるようにします。
怠惰なパラメータの初期化! 以前のすべての回答 (戦略パターン、オブザーバー パターンなど) に加えて、デリゲートを使用すると、パラメーターの遅延初期化を処理できます。たとえば、非常に時間がかかり、特定の DownloadedObject を返す関数 Download() があるとします。このオブジェクトは、特定の条件に応じてストレージによって消費されます。通常、次のことを行います。
storage.Store(conditions, Download(item))
ただし、デリゲート (より正確にはラムダ) を使用すると、 store の署名を変更して Condition と Func<Item,DownloadedObject> を受け取り、次のように使用することで、次のことができます。
storage.Store(conditions, (item) => Download(item))
したがって、ストレージは必要に応じてデリゲートのみを評価し、条件に応じてダウンロードを実行します。
In Array.Sort(T[]配列、比較比較)、List.Sort(比較比較)などの比較パラメータ
デリゲートの使用
- イベント処理
- マルチキャスティング
デリゲートは、参照によってメソッドを呼び出すために使用されます。例えば:
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();
}
}
使用法の最初の行は、Observer/Observable (イベント) パターンを置き換えることです。2 つ目は、Strategy パターンのエレガントなバージョンです。これらの最初の 2 つより難解だと思いますが、他にもさまざまな使用法を収集できます。
イベント、その他の任意の操作
動作をカプセル化したいが、統一された方法で呼び出したいとき。イベント ハンドラー、コールバック関数など。インターフェイスとキャストを使用して同様のことを実現できますが、動作が型やオブジェクトに必ずしも関連付けられていない場合があります。カプセル化する必要がある動作がある場合もあります。
私の知る限り、デリゲートは関数ポインターに変換できます。これにより、元のプログラマーがそのための準備をしていなくても、関数ポインターを効果的にオブジェクト指向にすることができるため、関数ポインターを受け取るネイティブ コードと相互運用する場合の作業が非常に簡単になります。
イベントハンドラ用
メソッド パラメータでメソッドを渡すには