我试图弄清楚为什么这不起作用:
public void DefaultAction( object obj = null ){}
public void Start()
{
SomeReferenceType obj;
DefaultAction( obj ); //works
int i;
string s;
DefaultAction( i ); //works
DefaultAction( s ); //works
}
//however...
public event Action OnNullAction = DefaultAction; //works
public event Action<SomeReferenceType> OnObjectAction = DefaultAction; //works
public event Action<int> OnIntAction = DefaultAction; //doesn't work!!
尝试将void(object)
绑定到Action<ValueType>
会引发参数不匹配错误,即使您可以使用int / string / bool直接调用该函数。是否有一些神秘的拳击/拆箱事件?无论如何,是否可以创建一个可以响应任何Action<T>
?
答案 0 :(得分:7)
您发现委托违规需要参考类型。
我知道,那是非常高调的。
首先,让我清楚地说明协方差和逆变是什么。假设您在类型之间存在关系:&#34;类型Giraffe
的值可以分配给类型为Animal
&#34;的变量。让我们将其标记为
Animal <-- Giraffe
如果用C<T>
替换每个类型保留箭头的方向,则T
中的泛型C<that type>
被称为协变。< / em>的
IEnumerable<Animal> <-- IEnumerable<Giraffe>
从C#4.0开始,当我将这个功能添加到语言中时,你可以在需要一系列动物的任何地方使用一系列长颈鹿。
如果替换反转箭头的方向,则C<T>
中的泛型T
被称为逆变:
Action<Animal> --> Action<Giraffe>
如果您需要一项操作,要求您为其提供Giraffe
,并且您有一项可以采取任何Animal
的操作,那么您已全部设定;你需要一些可以Giraffe
而Action<Animal>
可以选择Giraffe
的内容。但这不是协变。如果您手头有Action<Giraffe>
且需要Action<Animal>
,则无法使用Action<Giraffe>
,因为*您可以将Tiger
传递给{{1}但不是Action<Animal>
。
Action<Giraffe>
怎么样?它是Func<T>
中的协变量。如果你需要一个返回T
的函数并且你有一个返回Animal
的函数,那么你很好,因为Giraffe
将是Giraffe
。
Animal
怎么样?它是Func<A, R>
中的逆变量和A
中的协变量。应该清楚为什么。
现在我们知道泛型类型的协方差和逆变是什么,C#中的规则是什么?规则是:
类型声明必须使用R
(逆变)和in
(协变)进行注释。例如,out
。请注意,delegate R Func<in A, out R>(A a)
是进入函数的东西,in
是 out 函数的东西;我们故意将它们命名为out
和in
。
编译器必须能够证明注释是安全的。有关详细信息,请参阅规范或我的博客。
只有通用委托和接口支持差异,而不支持通用结构,枚举或类。
不同类型必须都是引用类型。
所以现在我们来回答你的问题。为什么他们都必须是参考类型?你推断出答案:拳击指令在哪里?
out
拳击教学在哪里?不在分配给Action<object> oa = (object x)=>whatever;
Action<int> ia = oa; // Suppose this works.
ia(123);
的lambda的主体中 - 该东西已经oa
。不在object
的调用中 - 那个东西需要一个整数。唯一可行的解决方案是ia(123)
和oa
不等;这是
ia
但如果这就是你的意思,那就说出来。人们期望引用转换将保持引用标识,因此C#禁止协变或逆变转换,这些转换必须对值进行装箱或取消装箱。
如果您对此有更多疑问,请搜索我的旧博客(blogs.msdn.com/ericlippert)以查找Action<object> oa = (object x)=>{whatever};
Action<int> ia = (int x)=>{ oa(x); };
或搜索C#协方差常见问题解答。
答案 1 :(得分:1)
让它变得通用
public void DefaultAction<T>(T param) { }