C#のデリゲートは概念的には理解していると思いますが、実際に役立つ例を見つけるのに苦労しています。 C#のデリゲートが実際のアプリケーションでどのように使用され、どのようにして問題を回避できるかについて詳しく説明してください。
GUIコードはデリゲートを使用して、ボタンのクリック、ウィンドウの移動などのイベントを処理します。デリゲートを使用すると、イベントが発生するたびに呼び出される関数を使用できます。例としては、データを保存する関数をインターフェースの「保存」ボタンにリンクする場合があります。ボタンがクリックされると、データを保存する機能を実行するように設定されます。 GUIプログラミングでは、プログラム全体がユーザーの操作を待っている可能性があり、ユーザーが最初に何をするかを知る方法がないため、便利です。デリゲートを使用すると、プログラムの機能をUIに接続して、ユーザーが好きなように操作できるようになります。
LinqはFunc<T>
およびAction<T>
あらゆる場所にパラメータとして委任します。
これらにより、ラムダ式をパラメーターとして使用し、パラメーターリストの一部として実行するアクションを定義できます。
Observer Pattern を使用する事実上すべてがデリゲートを実装する可能性があります。
説明を読むと、おそらくそれらを使用するいくつかのシナリオを想像するでしょう。 GUIイベント処理は一般的な例です。
デリゲートは非同期プログラミングで非常に便利です。
非同期で処理を行い、コールバックを持つクラスがあります。コールバック時にデリゲートメソッドを呼び出すことができます。クラスの実装は、デリゲートメソッドで記述されたロジックを実行します。
デリゲートは 中間パターンの穴 の解決策として特に役立ちます。基本的に、共通の命令の束の中に固有の命令セットをラップしたい場合がたくさんあります。一意のビットの前後の命令が状態を共有する必要がある場合は、特に困難です。デリゲートを使用すると、デリゲートを関数に渡すことができます。関数はbeforeビットを実行し、デリゲートを実行してから、afterビットを実行します。
FortranやCのような非OOP言語の「昔」では、サブルーチンが関数へのポインターである引数を受け取ることができると信じられないほど便利でした。たとえば、qsort
関数は、ユーザー提供の比較関数と連動します。常微分方程式を解く、または関数を最適化するための多数のサブルーチンがあり、それらはすべて引数として関数ポインターを取ります。
ウィンドウシステムでは、すべての種類のコールバックが同じパターンに従います。
LISPには、初期の頃でさえ、「関数引数」またはFUNARGと呼ばれるものがありました。これは関数であるだけでなく、記憶し、外界の一部と相互作用できるストレージコンテキストも含んでいました。
これと同じニーズがOOP言語に存在しますが、関数のアドレスを渡すときは、関数がメソッドであるオブジェクトのアドレスも渡す必要があります。それは、渡す必要がある2つのことです。 。したがって、デリゲートはまさにそれであり、その古き良きパターンを引き続き使用することができます。
DRYの原則に従う単純なコードを作成するときにデリゲートがどのように役立つかを示す簡単な例を次に示します。これにより、コードを必要な場所に非常に近づけることもできます。
Action<Button, Action<Button>> prepareButton =
(btn, nxt) => {
btn.Height = 32;
btn.Width= 64;
nxt(btn);
};
prepareButton(myBtn1, btn => btn.Text = "A");
prepareButton(myBtn2, btn => btn.Text = "B");
prepareButton(myBtn3, btn => btn.Text = "C");
これは、デリゲートが提供する利点の実際の例です。
protected override void PageInitialize()
{
const string selectCodeFormat = "javascript:selectCode('{0}', '{1}');";
const string onClick = "return toggleElement(this);";
Func<HtmlGenericControl> getElement = null;
Action<HtmlGenericControl> setElement = null, addChild = null;
HtmlGenericControl level1Element = null, level2Element = null, level3Element = null, level4Element = null;
string className = null, code = null, description = null;
using (var records = Core.Database.ExecuteRecords("code.SocCodeTree"))
{
while (records.Read())
{
code = records.GetString("Code");
description = records.GetString("Description");
if (records.GetString("Level4") != "")
{
className = "Level4";
setElement = e => level4Element = e;
getElement = () => level4Element;
addChild = e => level3Element.Controls.Add(e);
}
else if (records.GetString("Level3") != "")
{
className = "Level3";
setElement = e => level3Element = e;
getElement = () => level3Element;
addChild = e => level2Element.Controls.Add(e);
}
else if (records.GetString("Level2") != "")
{
className = "Level2";
setElement = e => level2Element = e;
getElement = () => level2Element;
addChild = e => level1Element.Controls.Add(e);
}
else
{
className = "Level1";
setElement = e => level1Element = e;
getElement = () => level1Element;
addChild = e => Root.Controls.Add(e);
}
var child = new HtmlGenericControl("li");
child.Attributes["class"] = className;
var span = new HtmlGenericControl("span") {
InnerText = code + " - " + description + " - "
};
span.Attributes["onclick"] = onClick;
child.Controls.Add(span);
var a = new HtmlAnchor() {
InnerText = "Select",
HRef = string.Format(selectCodeFormat, code, description)
};
child.Controls.Add(a);
setElement(new HtmlGenericControl("ul"));
child.Controls.Add(getElement());
addChild(child);
}
}
}
デリゲートとの最初の出会いは、自分のWebサイトからファイルをダウンロードしてプログラムの更新(WindowsフォームC#3.5)をチェックすることでしたが、更新チェックがプログラム全体をロックするのを避けるために、デリゲートとスレッドを使用して非同期に行いました。
デリゲートを効果的に使用するStrategyパターンの興味深い実装を見てきました。 (つまり、戦略はデリゲートです)
私が調べていたのは、パスを見つけるためのアルゴリズムが実行時に(再)割り当てられるデリゲートであり、異なるアルゴリズムを使用できる(BFSとA *など)パスファインディングに関するものでした。
従来のGoFパターンの多くはデリゲートを使用して実装できます。たとえば、コマンドパターン、訪問者パターン、戦略パターン、ファクトリーパターン、オブザーバーパターンは、多くの場合、単純なデリゲートを使用して実装できます。場合によっては、クラスの方が優れていることがあります(たとえば、コマンドに名前が必要な場合や、戦略オブジェクトをシリアル化する必要がある場合など)が、ほとんどの場合、Action<...>
またはFunc<...>
は、専用の1メソッドインターフェースを作成するよりもはるかにエレガントです。