首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ArgumentException:向事件添加协变方法时不兼容的委托类型

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

Stack Overflow用户
提问于 2019-08-07 03:44:39
回答 2查看 116关注 0票数 1

当我试图添加两个具有不同签名的方法时,我看到了非常奇怪的行为(这两个方法是相互协变的)。当我尝试添加第二个方法时,它抛出了一个ArgumentException: Incompatible Delegate Types

代码语言:javascript
运行
复制
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;
}

基本上是一个简单的泛型接口,其中泛型参数是协变的,一个为泛型分配类型的子接口,以及一个子接口的实现。

下面是抛出异常的代码:

代码语言:javascript
运行
复制
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) { }

现在代码可以很好地编译,并且只添加更具体或更通用的方法,每个方法都可以单独工作,但是如果我尝试同时添加这两个方法,就会得到异常。

这说不通。知道为什么吗?有什么解决方案吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-08-09 06:17:54

我仍然没有找到为什么会发生这种情况的解释,但我确实找到了一种解决方法,可以让你做到这一点。您必须实现对事件的访问器,并将委托保存在单独的列表或散列集中,而不是使用内置的事件实现。

代码语言:javascript
运行
复制
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,它都会工作并且不会抛出异常。

票数 0
EN

Stack Overflow用户

发布于 2019-08-07 04:15:28

对于可能的解决方案,您可以使用特定类型的引用来添加两个处理程序,由于协方差,它工作得很好:

代码语言:javascript
运行
复制
impObj.doSomethingWithT += DoSomethingSpecific; 
impObj.doSomethingWithT += DoSomething; 

至于原因,我只能提供一个有根据的猜测:运行库将不允许具有不同类型参数的处理程序附加到具有泛型类型的委托,即使协方差规则对于编译器而言是有效的。而泛型类型(System.Action<T>)的委托正是您在使用genericObj引用时要访问的委托,即使它在创建impObj时已经用具体的参数类型进行了初始化。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57382990

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档