この質問は10年以上前のものであることは承知していますが、最も明白な答えが扱われていないだけでなく、質問からは何が隠されているのかをよく理解しているとは思えないかもしれません。さらに、遅延バインディング、およびデリゲートとラムダに関してそれが何を意味するかについては他にも質問があります(詳細は後ほど)。
まず、部屋で800ポンドのゾウ/ゴリラに対処しますevent。vs Action<T>/ を選択するタイミングはFunc<T>次のとおりです。
- ラムダを使用して1つのステートメントまたはメソッドを実行します。
event実行する複数のステートメント/ラムダ/関数を含むpub / subモデルをさらに必要とする場合に使用します(これは
すぐに大きな違いです)。
- ステートメント/関数を式ツリーにコンパイルする場合は、ラムダを使用します。リフレクションやCOM相互運用で使用されるような、より伝統的な遅延バインディングに参加したい場合は、デリゲート/イベントを使用します。
イベントの例として、次のように小さなコンソールアプリケーションを使用して、シンプルで「標準的な」イベントのセットを結び付けます。
public delegate void FireEvent(int num);
public delegate void FireNiceEvent(object sender, SomeStandardArgs args);
public class SomeStandardArgs : EventArgs
{
public SomeStandardArgs(string id)
{
ID = id;
}
public string ID { get; set; }
}
class Program
{
public static event FireEvent OnFireEvent;
public static event FireNiceEvent OnFireNiceEvent;
static void Main(string[] args)
{
OnFireEvent += SomeSimpleEvent1;
OnFireEvent += SomeSimpleEvent2;
OnFireNiceEvent += SomeStandardEvent1;
OnFireNiceEvent += SomeStandardEvent2;
Console.WriteLine("Firing events.....");
OnFireEvent?.Invoke(3);
OnFireNiceEvent?.Invoke(null, new SomeStandardArgs("Fred"));
//Console.WriteLine($"{HeightSensorTypes.Keyence_IL030}:{(int)HeightSensorTypes.Keyence_IL030}");
Console.ReadLine();
}
private static void SomeSimpleEvent1(int num)
{
Console.WriteLine($"{nameof(SomeSimpleEvent1)}:{num}");
}
private static void SomeSimpleEvent2(int num)
{
Console.WriteLine($"{nameof(SomeSimpleEvent2)}:{num}");
}
private static void SomeStandardEvent1(object sender, SomeStandardArgs args)
{
Console.WriteLine($"{nameof(SomeStandardEvent1)}:{args.ID}");
}
private static void SomeStandardEvent2(object sender, SomeStandardArgs args)
{
Console.WriteLine($"{nameof(SomeStandardEvent2)}:{args.ID}");
}
}
出力は次のようになります。

Action<int>またはAction<object, SomeStandardArgs>で同じことをした場合、とだけが表示さSomeSimpleEvent2れSomeStandardEvent2ます。
では、何が起こっているのeventでしょうか?
展開するFireNiceEventと、コンパイラは実際に次のものを生成します(この説明に関係のないスレッド同期に関する詳細は省略しています)。
private EventHandler<SomeStandardArgs> _OnFireNiceEvent;
public void add_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
{
Delegate.Combine(_OnFireNiceEvent, handler);
}
public void remove_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
{
Delegate.Remove(_OnFireNiceEvent, handler);
}
public event EventHandler<SomeStandardArgs> OnFireNiceEvent
{
add
{
add_OnFireNiceEvent(value)
}
remove
{
remove_OnFireNiceEvent(value)
}
}
コンパイラーは、それが生成されるクラス名前空間からは見えないプライベートデリゲート変数を生成します。そのデリゲートは、サブスクリプション管理と遅延バインディングの参加のために使用されるものであり、公共の対面インタフェースは精通している+=と-=オペレータ、我々はすべて知っているようになったと愛:)
FireNiceEventデリゲートのスコープを保護に変更することにより、追加/削除ハンドラーのコードをカスタマイズできます。これにより、開発者はロギングやセキュリティフックなどのカスタムフックをフックに追加できます。これは、ユーザーの役割などに基づいてサブスクリプションへのカスタマイズされたアクセシビリティを可能にするいくつかの非常に強力な機能を実際にもたらします。ラムダでそれを行うことができますか?(実際には、式ツリーをカスタムコンパイルすることでできますが、それはこの応答の範囲外です)
ここでいくつかの応答からいくつかのポイントに対処するには:
のargsリストAction<T>を変更することと、から派生したクラスのプロパティを変更することの「脆弱性」に違いはありませんEventArgs。どちらもコンパイルの変更を必要とするだけでなく、両方ともパブリックインターフェイスを変更し、バージョン管理を必要とします。変わりはない。
どちらが業界標準であるかに関しては、これが使用されている場所と理由によって異なります。Action<T>IoCやDIでeventよく使用され、GUIやMQタイプのフレームワークなどのメッセージルーティングでよく使用されます。いつもではなく、頻繁に言ったことに注意してください。
デリゲートには、ラムダとは異なる有効期間があります。捕獲についても意識する必要があります...閉鎖だけでなく、「猫が引きずり込んだものを見る」という概念も必要です。これは、メモリのフットプリント/寿命だけでなく、リークの管理にも影響します。
もう1つ、以前に参照したもの...遅延バインディングの概念。ラムダがいつ「ライブ」になるかに関して、LINQのようなフレームワークを使用するときに、これがよく見られます。これは、複数回発生する可能性があるデリゲートのレイトバインディングとは大きく異なります(つまり、ラムダは常に存在しますが、バインディングは必要に応じてオンデマンドで発生します)。 -魔法はなくなり、メソッド/プロパティは常にバインドされます。覚えておくべきこと。