当我试图添加两个具有不同签名的方法时,我看到了非常奇怪的行为(这两个方法是相互协变的)。当我尝试添加第二个方法时,它抛出了一个ArgumentException: Incompatible Delegate Types。
public class SomeClass { } // Just a class that inherits from object
public interface GenericInterface<out T> { // An interface with a covariant parameter T
event System.Action<T> doSomethingWithT;
}
public interface SpecificInterface : GenericInterface<SomeClass> { } // A more specific interface where T = SomeClass
public class ImpClass: SpecificInterface { // An implementation of the more specific interface
public event System.Action<SomeClass> doSomethingWithT;
}基本上是一个简单的泛型接口,其中泛型参数是协变的,一个为泛型分配类型的子接口,以及一个子接口的实现。
下面是抛出异常的代码:
protected void Start() {
ImpClass impObj = new ImpClass();
GenericInterface<object> genericObj = impObj; // assignment possible because interface is covariant
impObj.doSomethingWithT += DoSomethingSpecific;
genericObj.doSomethingWithT += DoSomething; // this line throws an exception
}
protected void DoSomething(object o) { }
protected void DoSomethingSpecific(SomeClass o) { }现在代码可以很好地编译,并且只添加更具体或更通用的方法,每个方法都可以单独工作,但是如果我尝试同时添加这两个方法,就会得到异常。
这说不通。知道为什么吗?有什么解决方案吗?
发布于 2019-08-09 06:17:54
我仍然没有找到为什么会发生这种情况的解释,但我确实找到了一种解决方法,可以让你做到这一点。您必须实现对事件的访问器,并将委托保存在单独的列表或散列集中,而不是使用内置的事件实现。
public class ImpClass: SpecificInterface { // An implementation of the more specific interface
public event System.Action<SomeClass> doSomethingWithT {
add { delegateSubs.Add(value); }
remove { delegateSubs.Remove(value); }
}
protected HashSet<System.Action<SomeClass>> delegateSubs = new HashSet<System.Action<SomeClass>>();
}这样,你就可以毫无问题地添加/删除T的多个基类型的委托。缺点当然是你必须为实现接口的每个类这样做,但它保证无论何时你使用这些类的事件而不考虑T,它都会工作并且不会抛出异常。
发布于 2019-08-07 04:15:28
对于可能的解决方案,您可以使用特定类型的引用来添加两个处理程序,由于协方差,它工作得很好:
impObj.doSomethingWithT += DoSomethingSpecific;
impObj.doSomethingWithT += DoSomething; 至于原因,我只能提供一个有根据的猜测:运行库将不允许具有不同类型参数的处理程序附加到具有泛型类型的委托,即使协方差规则对于编译器而言是有效的。而泛型类型(System.Action<T>)的委托正是您在使用genericObj引用时要访问的委托,即使它在创建impObj时已经用具体的参数类型进行了初始化。
https://stackoverflow.com/questions/57382990
复制相似问题