如何委派正确的Action <interface>?</interface>

时间:2012-06-10 15:58:16

标签: c# .net interface delegates compiler-errors

我是C#的初学者,找不到任何答案:

我试图用一些接口参数委托Actions,但是用扩展这个接口(或类)的对象推送函数

// some class extending interface
public class IntEvent : IFace{}
public class MoveEvent : IFace{}

// function i like to use to verify Action
static void setAction(Action<IFace> evt) {
    // evt ...
}
// function to delegate as Action
static void evtCheck(IntEvent evt) {
    // some func
}
static void evtMove(MoveEvent evt) {
    // some func
}
// class {
//  call method inside class and delegate this function :
setAction(evtCheck);
setAction(evtMove);

我收到的错误是“evtCheck(IntEvent)无法转换为Action<IFace>”,即使IntEvent扩展了IFace界面。

我该如何解决这个问题?也许我必须使用Func或Delegate?

2 个答案:

答案 0 :(得分:7)

你无法做你想做的事情 - 你期待协方差,但Action<T>逆变唯一的类型参数。

您无法执行从evtCheckAction<IFace>的方法组转换,因为evtCheck需要更具体的类型(IntEvent)作为其参数而不是IFace实例期望的更一般的Action<IFace>类型。如果允许这样的转换,如果委托是使用实现IFace但不是IntEvent的参数执行的,那么您期望会发生什么?

我认为你应该再看看你的设计,因为它看起来有一个缺陷,但是如果你想要,你可以创建一个lambda来强制将参数强制转换为所需类型并接受{的可能性{1}}:

InvalidCastException

更有可能的是,您可能希望setAction(iFace => evtCheck((IntEvent)iface)); 接受更通用的类型,或evtCheck接受更具体的代表。

答案 1 :(得分:5)

Action<T>在T中是逆变的,因此采用Action<T>的方法也会接受Action<U>,其中T来自U.

然而,您的IntEvent类实现了IFace接口,这意味着如果编译器接受了您的setAction()调用,则可以使用evtCheck()调用IFace参数是另一个IntEvent - 衍生而不是IntEvent,运行时错误和明显违反类型安全。

必要的Eric Lippert参考:Covariance and contravariance

编辑:根据您的描述,在我看来,您真正想要的是基于方法参数类型的区分,例如,你想为DoubleEvent单独执行一个操作,为(假设的){{1}}等另一个操作。如果是这种情况,你实际上是在双虚拟调度之后,C#中没有直接支持,但是你可以使用Visitor设计模式进行模拟。