![](/img/trans.png)
[英]C# Generics : CS0311 when implementing generic class that meets a generic interface contract
[英]Odd C# behavior when implementing generic interface
给定这个“IHandle”接口和两个要处理的类:
interface IHandle<T>
{
void Handle(T m);
}
class M1
{
public int Id;
}
class MReset
{
}
我想创建一个通用基础,负责“重置”以及管理M1实例:
class HandlerBase<T> :
IHandle<MReset>,
IHandle<T> where T : M1
{
protected int Count;
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("{0}: Count = {0}", m.Id, Count);
}
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
}
}
这不编译,因为编译器认为T可以是“MReset”,因此它输出:
错误CS0695:'HandlerBase'无法实现'IHandle'和'IHandle',因为它们可能会统一某些类型参数替换
这本身就有些奇怪,因为我无法看到T可能是MReset类型,因为它必须是M1类型。 但好吧,我可以接受编译器比我聪明:-)
编辑 :编译器并不比我聪明:-)根据评论为什么这会导致CS0695? 我们有“在确定所有可能的构造类型时不考虑约束声明”。
现在我交换接口声明:
class HandlerBase<T> :
IHandle<T> where T : M1,
IHandle<MReset>
{
... same as before ..
}
突然间我收到一条不同的错误消息,指出我无法实现IHandle.Handle(MReset m),因为类声明没有声明它正在实现该接口:
错误CS0540:'HandlerBase.IHandle <...>。句柄(MReset)':包含类型没有实现接口'IHandle'
问题 :为什么声明的顺序如此不同? 第二个例子出了什么问题?
最后证明有一个解决方案:
class HandlerBase :
IHandle<MReset>
{
protected int Count;
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
}
}
class Handler<T> : HandlerBase,
IHandle<T> where T : M1
{
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("{0}: Count = {0}", m.Id, Count);
}
}
但是该解决方案仅在HandlerBase
实现IHandle<MReset>
时才有效 - 如果通用接口IHandle<T>
首先在HandlerBase
实现则IHandle<T>
。 为什么 ?
编辑 :在HandlerBase
中实现IHandle<T>
确实有效(如果我已经显示了某些人可能已经看过它的代码)。 这有效:
class HandlerBase<T> :
IHandle<T> where T : M1
{
protected int Count;
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("Type = {0}, Id = {1}, Count = {2}", GetType(), m.Id, Count);
}
}
class Handler<T> : HandlerBase<T>,
IHandle<MReset>
where T : M1
{
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
Console.WriteLine("RESET");
}
}
不幸的是,我的第二课声明是:
class Handler<T> : HandlerBase<T> where T : M1,
IHandle<MReset>
{
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
Console.WriteLine("RESET");
}
}
注意where T : M1
:-)位置的细微差别最后一个例子声明T必须实现IHandle<MReset>
(除M1
)。 咄!
@Siram指出,唯一性问题(但不是订单方面)已经回答了为什么这会导致CS0695? :
C#语言规范( https://www.microsoft.com/en-us/download/confirmation.aspx?id=7029 )讨论了13.4.2中“已实现接口的唯一性”:“通用实现的接口对于所有可能的构造类型,类型声明必须保持唯一。“ 稍后,在描述检查的细节时: “在确定所有可能的构造类型时,不考虑约束声明 。”
为什么会这样,我不确定; 或许可以构造嵌套或链式约束,这使得编译器无法证明唯一性,或者不能通过程序集传递所有约束(我认为这对于通用语言规则是必要的)。
问题解决了 - 我发现了微妙的差异。 当声明的顺序被交换我不能移动where T : M1
由于IHandle<MReset>
约束,则最终被施加至T代替类声明:
class HandlerBase<T> :
IHandle<T> where T : M1,
IHandle<MReset>
{
... same as before ..
}
正确的重新排序应该是:
class HandlerBase<T> :
IHandle<T>,
IHandle<MReset>
where T : M1
{
... same as before ..
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.