为什么C#编译器在从不同的基类派生时抱怨“types可能会统一”?
我目前的非编译代码与此类似:
public abstract class A { } public class B { } public class C : A { } public interface IFoo<T> { void Handle(T item); } public class MyFoo<TA> : IFoo<TA>, IFoo<B> where TA : A { public void Handle(TA a) { } public void Handle(B b) { } }
C#编译器拒绝编译这个,引用下面的规则/错误:
'MyProject.MyFoo <TA>'不能实现'MyProject.IFoo <TA>'和'MyProject.IFoo <MyProject.B>',因为它们可能会统一一些types参数replace
我明白这个错误的含义 如果TA
可以是任何东西,那么它在技术上也可能是B
,这会在两个不同的Handle
实现中引入歧义。
但是TA 不可能是任何东西。 基于types层次结构, TA
不可能是B
– 至less我不这么认为 。 TA
必须从A
派生,而B
不是从B
派生的,显然在C#/。NET中没有多重类inheritance。
如果我删除通用参数,并用C
或A
replaceTA
,则编译。
那么为什么我会得到这个错误? 它是编译器中的一个错误还是一般的非智能,还是还有别的东西我错过了?
是否有任何解决方法,或者我将不得不重新实现MyFoo
generics类作为一个单独的非generics类为每个可能的TA
派生types?
这是C#4规范的第13.4.2节的一个结果,它指出:
如果从C创build的任何可能的构造types将types参数replace为L之后,导致L中的两个接口相同,则C的声明无效。 确定所有可能的构造types时,不考虑约束声明。
注意那里的第二句话。
因此它不是编译器中的一个错误。 编译器是正确的。 有人可能会认为这是语言规范中的一个缺陷。
一般来说,在几乎所有的情况下,约束都被忽略,在这种情况下必须推导一个关于generics的事实。 约束主要用于确定genericstypes参数的有效基类 ,而其他的则很less。
不幸的是,有时会导致语言不必要的严格的情况,就像你发现的那样。
通常两次实现“相同”接口的代码味道很差,在某种程度上只能通过genericstypes参数来区分。 例如class C : IEnumerable<Turtle>, IEnumerable<Giraffe>
– C是什么,它既是一串乌龟又是一串长颈鹿, 同时呢? 你能描述一下你在这里想要做的事吗? 解决真正的问题可能有更好的模式。
如果实际上你的界面和你描述的一模一样:
interface IFoo<T> { void Handle(T t); }
然后,接口的多重inheritance存在另一个问题。 你可能会合理地决定使这个接口是逆向的:
interface IFoo<in T> { void Handle(T t); }
现在假设你有
interface IABC {} interface IDEF {} interface IABCDEF : IABC, IDEF {}
和
class Danger : IFoo<IABC>, IFoo<IDEF> { void IFoo<IABC>.Handle(IABC x) {} void IFoo<IDEF>.Handle(IDEF x) {} }
现在事情变得非常疯狂
IFoo<IABCDEF> crazy = new Danger(); crazy.Handle(null);
Handle的哪个实现被调用 ?
有关此问题的更多信息,请参阅本文和注释。
显然这是在Microsoft Connect上讨论的devise:
- 允许在某些条件下为通用类中的多个types参数实现相同的通用接口
解决方法是,定义另一个接口为:
public interface IIFoo<T> : IFoo<T> { }
然后将其实现为:
public class MyFoo<TA> : IIFoo<TA>, IFoo<B> where TA : A { public void Handle(TA a) { } public void Handle(B b) { } }
它现在编译好, 单声道 。
看到我的回答基本上相同的问题在这里: https : //stackoverflow.com/a/12361409/471129
在某种程度上,这可以做到! 我使用区分方法,而不是限定types的限定符。
它并不统一,实际上它可能比如果这样做更好,因为你可以把单独的接口分开。
在这里看到我的文章,在另一个上下文中有一个完整的工作示例 https://stackoverflow.com/a/12361409/471129
基本上,你所做的就是向IIndexer添加另一个types参数,使它成为IIndexer <TKey, TValue, TDifferentiator>
。
那么当你使用它两次,你通过第一次使用“第一”,第二次使用“秒”
所以,类Test成为:class Test<TKey, TValue> : IIndexer<TKey, TValue, First>, IIndexer<TValue, TKey, Second>
因此,你可以做new Test<int,int>()
第一,第二是微不足道的:
interface First { } interface Second { }
我知道这个post已经发布了一段时间,但是对于那些通过search引擎寻求帮助的人来说, 请注意,“Base”代表TA和B的基类。
public class MyFoo<TA> : IFoo<Base> where TA : Base where B : Base { public void Handle(Base obj) { if(obj is TA) { // TA specific codes or calls } else if(obj is B) { // B specific codes or calls } } }
现在猜测…
A,B和C不能在外部程序集中声明,在编译MyFoo <T>之后types层次可能会改变,给世界带来破坏?
简单的解决方法是实现句柄(A)而不是句柄(TA)(并使用IFoo <A>而不是IFoo <TA>)。 无论如何,您不能使用Handle(TA)做比A的访问方法更多的操作(由于A:TA限制)。
public class MyFoo : IFoo<A>, IFoo<B> { public void Handle(A a) { } public void Handle(B b) { } }
嗯,这个怎么样:
public class MyFoo<TA> : IFoo<TA>, IFoo<B> where TA : A { void IFoo<TA>.Handle(TA a) { } void IFoo<B>.Handle(B b) { } }
- C ++ std :: pair的C#模拟是什么?
- 当两个types参数相同时,在两个构造函数之间消除歧义
- 如何将一个stringparsing为一个可空的int
- 初始化VCProjectEngineObject(Microsoft.VisualStudio.VCProjectEngine.dll)时出现错误80040154(类未注册的exception)
- 为什么我的ClaimsIdentity总是被authentication为false(对于web api授权filter)?
- 等待vs Task.Wait – 死锁?
- 我应该在这种情况下使用嵌套类吗?
- 无法为类库中的entity framework启用迁移
- VB.net中的“内部”是什么?