Why does using Delegate.Combine require a cast but

2019-06-22 04:58发布

问题:

I couldn't find the + operator in Reflector under Delegate or MulticastDelegate.

I'm trying to figure out how this doesn't need a cast:

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

Action c = a + b;

But this does:

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

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

In the first sample is the cast just done under the covers?

回答1:

Taking the following example:

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();
    }
}

Then looking at it with 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();
    }
}

Seems that they do the exact same thing, just C# is providing some syntactic sugar in the first test.



回答2:

+= and -= is implemented at the language level (i.e. with compiler help) with known delegate types, so it doesn't need a cast, whereas Delegate.Combine is just an ordinary (non-generic) method with Delegate return type, so it needs a cast.



回答3:

In the first sample is the cast just done under the covers?

Yes, you can say that!

The Combine method was written in .NET 1 where no generic C# existed. The formal return type of Combine therefore had to be Delegate:

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

But the method still returns an Action when both a and b are Action. And yes, a and b are required to have the same runtime type.

Please don't write MulticastDelegate.Combine as Combine is a static method defined by the System.Delegate class. Therefore say Delegate.Combine, that's less confusing.

Detour:

The current version of C# and Combine has problems with contravariant delegate types. Consider the following:

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

Now, suppose some future version of the framework introduced a generic Combine method:

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

and suppose C# was changed such that + was translated into a call to the new generic Combine<> method, then contravariance and delegate combination would be fixed! I guess they tell us they have higher priorities, but still.



回答4:

Its done using "operator overloading", you can do this in your own objects aswell.

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;

Result: o3.Property = "Hello World!"

So I assume the framwork performs somthing like this in the background

    public static Action operator +(Action a1, Action a2)
    {
        return (Action)MulticastDelegate.Combine(a1,a2);
    }


标签: c# delegates