为什么使用 Delegate.Combine 需要强制转换,而使用 + 运算符不需要?



我在DelegateMulticastDelegate下的Reflector中找不到+运算符。

我正试图弄清楚这怎么不需要演员阵容:

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

在第一个样品中,铸件是在盖子下铸造的吗?

+=-=是在语言级别(即在编译器的帮助下)用已知的委托类型实现的,因此它不需要强制转换,而Delegate.Combine只是一个具有Delegate返回类型的普通(非泛型)方法,因此它需要强制转换。

以以下示例为例:

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#在第一次测试中提供了一些语法糖。

在第一个示例中,是否只是在封面下完成了铸造?

是的,你可以这么说!

Combine方法是在.NET 1中编写的,其中不存在通用C#。因此,Combine的正式返回类型必须是Delegate:

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

但是当ab都是Action时,该方法仍然返回Action。是的,ab需要具有相同的运行时类型。

请不要写MulticastDelegate.Combine,因为Combine是由System.Delegate类定义的static方法。因此,说Delegate.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<>方法的调用,那么逆变和委托组合将被固定我想他们告诉我们他们有更高的优先级,但仍然如此。

它使用"运算符重载"完成,您也可以在自己的对象中完成。

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

最新更新