ArgumentException:向事件添加协变方法时出现不兼容的委托类型

当尝试添加具有不同签名的两种方法(彼此是协变的)时,我看到非常奇怪的行为。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) { }

现在代码可以正常编译,并且仅添加更具体的方法或仅添加更通用的方法,每个方法都可以单独正常工作,但如果我尝试添加两者,则会出现异常。


没有道理。知道为什么吗?以及有什么解决办法吗?


慕雪6442864
浏览 87回答 2
2回答

慕姐8265434

至于可能的解决方案,您可以使用特定类型的引用来添加两个处理程序,并且由于协方差,它可以正常工作:impObj.doSomethingWithT&nbsp;+=&nbsp;DoSomethingSpecific;&nbsp; impObj.doSomethingWithT&nbsp;+=&nbsp;DoSomething;至于原因,我只能提供一个有根据的猜测:运行时不允许将具有不同类型参数的处理程序附加到具有泛型类型的委托,即使协方差规则对于编译器而言是有效的。泛型类型 (&nbsp;System.Action<T>) 的委托正是您在使用genericObj引用时所访问的对象,即使它在创建 时已使用具体参数类型进行了初始化impObj。

沧海一幻觉

我仍然没有找到为什么会发生这种情况的解释,但我确实找到了一种解决方法,可以让您做到这一点。您必须实现事件的访问器并将委托保存在单独的列表或哈希集中,而不是使用内置事件实现。public class ImpClass: SpecificInterface {&nbsp; // An implementation of the more specific interface&nbsp; &nbsp; public event System.Action<SomeClass> doSomethingWithT {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; add { delegateSubs.Add(value); }&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; remove { delegateSubs.Remove(value); }&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; protected HashSet<System.Action<SomeClass>> delegateSubs = new HashSet<System.Action<SomeClass>>();}这样您就可以毫无问题地添加/删除 T 的多个基本类型的委托。当然,缺点是您必须为每个实现该接口的类执行此操作,但它保证每当您使用这些类的事件时,无论 T 如何,它都会起作用并且不会引发异常。
打开App,查看更多内容
随时随地看视频慕课网APP