[c#] .NET 4.0およびC#4.0でのイベントと委任contravariance


Answers

私はちょうど私のアプリケーションでこれを修正する必要がありました。 私は次のことをしました:

// variant delegate with variant event args
MyEventHandler<<in T>(object sender, IMyEventArgs<T> a)

// class implementing variant interface
class FiresEvents<T> : IFiresEvents<T>
{
    // list instead of event
    private readonly List<MyEventHandler<T>> happened = new List<MyEventHandler<T>>();

    // custom event implementation
    public event MyEventHandler<T> Happened
    {
        add
        {
            happened.Add(value);
        }
        remove
        {
            happened.Remove(value);
        }
    }

    public void Foo()
    {
        happened.ForEach(x => x.Invoke(this, new MyEventArgs<T>(t));
    }
}

通常のマルチキャストイベントに関連する違いがあるかどうかは分かりません。 私が使った限り、それは機能します...

ところで、 私はC#のイベントが一度も好きではありませんでした 。 なぜ言語機能があるのか​​理解できません。何の利点もありません。

Question

この質問を調べているうちに、C#4.0の新しい共分散/反分散機能がどのように影響を与えるのかが不思議に思えます。

ベータ1では、C#はCLRに同意しないようです。 C#3.0に戻る:

public event EventHandler<ClickEventArgs> Click;

...そしてあなたが持っていたどこかに:

button.Click += new EventHandler<EventArgs>(button_Click);

コンパイラは互換性のないデリゲート型であるため、barfになります。 しかし、C#4.0では、CLR 4.0ではtypeパラメータがinとしてマークさinているので、反変です。したがって、コンパイラはマルチキャストデリゲート+=が動作すると仮定しています。

私のテストはここにあります:

public class ClickEventArgs : EventArgs { }

public class Button
{
    public event EventHandler<ClickEventArgs> Click;

    public void MouseDown()
    {
        Click(this, new ClickEventArgs());
    }
}

class Program
{    
    static void Main(string[] args)
    {
        Button button = new Button();

        button.Click += new EventHandler<ClickEventArgs>(button_Click);
        button.Click += new EventHandler<EventArgs>(button_Click);

        button.MouseDown();
    }

    static void button_Click(object s, EventArgs e)
    {
        Console.WriteLine("Button was clicked");
    }
}

しかし、それはコンパイルされますが、実行時には機能しません( ArgumentException :デリゲートは同じ型でなければなりません)。

2つのデリゲートタイプのどちらか一方だけを追加しても問題ありません。 しかし、マルチキャストで2つの異なるタイプを組み合わせると、2つ目のタイプが追加されたときに例外が発生します。

私はこれがベータ1のCLRのバグだと思う(コンパイラの動作はうまくいけばうまくいく)。

リリース候補の更新:

上記のコードはもうコンパイルされません。 EventHandler<TEventArgs>デリゲートタイプのTEventArgsの反TEventArgsがロールバックされている必要があります。そのため、デリゲートは.NET 3.5と同じ定義になりました。

つまり、私が見ていたベータ版には、

public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);

今度は戻ってきた:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

しかし、 Action<T>デリゲートパラメータTはまだ反変的です:

public delegate void Action<in T>(T obj);

Func<T>Tが共変する場合も同じことがFunc<T>ます。

この妥協は、マルチキャストデリゲートの主な使用がイベントのコンテキスト内にあると仮定する限り、非常に意味があります。 私は個人的には、イベントとして以外のマルチキャストデリゲートを使用していないことがわかりました。

ですから、C#のコーディング標準では、新しいルールを採用できるようになりました。共分散/反差別によって複数のデリゲートタイプからマルチキャストデリゲートを作成しないでください。 それが何を意味するのかわからない場合は、イベントを安全な側にするためにActionを使用しないでください。

もちろん、その結論は、元の質問に影響を与えます。






Related