是否可以为* any * Action <t>?</t>创建委托

时间:2014-06-21 13:43:59

标签: c# events parameters delegates action

我试图弄清楚为什么这不起作用:

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>

的委托

2 个答案:

答案 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的操作,那么您已全部设定;你需要一些可以GiraffeAction<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 函数的东西;我们故意将它们命名为outin

  • 编译器必须能够证明注释是安全的。有关详细信息,请参阅规范或我的博客。

  • 只有通用委托和接口支持差异,而不支持通用结构,枚举或类。

  • 不同类型必须都是引用类型。

所以现在我们来回答你的问题。为什么他们都必须是参考类型?你推断出答案:拳击指令在哪里?

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