5

Delegateまたはの下の Reflector に + 演算子が見つかりませんでしたMulticastDelegate

これがキャストを必要としない方法を理解しようとしています:

Action a = () => Console.WriteLine("A");
Action b = () => Console.WriteLine("B");

Action c = a + b;

しかし、これは:

Action a = () => Console.WriteLine("A");
Action b = () => Console.WriteLine("B");

Action c = (Action)MulticastDelegate.Combine(a, b);

最初のサンプルでは、​​カバーの下でキャストが行われていますか?

4

4 に答える 4

5

+=既知のデリゲート型を使用して言語レベルで-=(つまり、コンパイラの助けを借りて) 実装されるため、キャストは必要ありませんが、戻り値の型Delegate.Combineを持つ通常の (非ジェネリック) メソッドであるため、キャストが必要です。Delegate

于 2012-11-25T09:19:53.397 に答える
1

最初のサンプルでは、​​カバーの下でキャストが行われていますか?

はい、そう言えます!

このCombineメソッドは、汎用 C# が存在しない .NET 1 で作成されました。したがって、 の正式な戻り値の型は次のCombineようにする必要がありましたDelegate

public static Delegate Combine(Delegate a, Delegate b)
{
  ...
}

ただし、このメソッドは、とのAction両方が である場合でもを返します。はい、同じランタイム タイプである必要があります。abActionab

クラスで定義されたメソッドをMulticastDelegate.Combineそのまま書かないでください。したがって、混乱が少ないと言えます。CombinestaticSystem.DelegateDelegate.Combine

迂回:

C# の現在のバージョンではCombine、反変のデリゲート型に問題があります。次の点を考慮してください。

Action<string> doSomethingToString; // will be assigned below

Action<ICloneable> a = cloneable => { Console.WriteLine("I'm cloning"); cloneable.Clone(); }
Action<IConvertible> b = convertible => { Console.WriteLine("I'm converting"); convertible.ToInt32(CultureInfo.InvariantCulture); }

Action<string> aStr = a; // OK by contravariance of Action<in T>, aStr and a reference same object
Action<string> bStr = b; // OK by contravariance of Action<in T>, bStr and b reference same object

doSomethingToString = aStr + bStr;  // throws exception
doSomethingToString("42");          // should first clone "42" then convert "42" to Int32

ここで、フレームワークの将来のバージョンでジェネリックCombineメソッドが導入されたとします。

public static TDel Combine<TDel>(TDel a, TDel b) where TDel : Delegate
{
  // use typeof(TDel) to figure out type of new "sum" delegate
}

C# が変更され、それが新しいジェネリック メソッド+の呼び出しに変換されたと仮定すると、反変性とデリゲートの組み合わせが修正されます。彼らは優先順位が高いと言っていると思いますが、それでもなおです。Combine<>

于 2012-11-25T09:20:27.390 に答える
1

次の例を取り上げます。

class Program
{
    static void Main(string[] args)
    {
        Test1();
        Test2();
    }

    public static void Test1()
    {
        Action a = () => Console.WriteLine("A");
        Action b = () => Console.WriteLine("B");

        Action c = a + b;
        c();
    }

    public static void Test2()
    {
        Action a = () => Console.WriteLine("A");
        Action b = () => Console.WriteLine("B");

        Action c = (Action)MulticastDelegate.Combine(a, b);
        c();
    }
}

次に、ILSpy で確認します。

internal class Program
{
    private static void Main(string[] args)
    {
        Program.Test1();
        Program.Test2();
    }
    public static void Test1()
    {
        Action a = delegate
        {
            Console.WriteLine("A");
        };
        Action b = delegate
        {
            Console.WriteLine("B");
        };
        Action c = (Action)Delegate.Combine(a, b);
        c();
    }
    public static void Test2()
    {
        Action a = delegate
        {
            Console.WriteLine("A");
        };
        Action b = delegate
        {
            Console.WriteLine("B");
        };
        Action c = (Action)Delegate.Combine(a, b);
        c();
    }
}

彼らはまったく同じことをしているように見えますが、最初のテストで C# が構文糖衣を提供しているだけです。

于 2012-11-25T09:23:45.203 に答える
0

「演算子のオーバーロード」を使用して行われます。これは、独自のオブジェクトでも行うことができます。

public class MyObject
{
    public string Property { get; set; }

    public MyObject(string property)
    {
        this.Property = property;
    }

    public static MyObject operator +(MyObject a1, MyObject a2)
    {
        return new MyObject(a1.Property + a2.Property);
    }
}

    MyObject o1 = new MyObject("Hello");
    MyObject o2 = new MyObject(" World!");

    MyObject o3 = o1 + o2;

結果: o3.Property = "Hello World!"

したがって、フレームワークはバックグラウンドでこのようなことを実行すると思います

    public static Action operator +(Action a1, Action a2)
    {
        return (Action)MulticastDelegate.Combine(a1,a2);
    }
于 2012-11-25T09:33:42.520 に答える