C# 为什么C“编译器会抱怨”;类型可以统一;当它们派生自不同的基类时?
我当前的非编译代码与此类似:C# 为什么C“编译器会抱怨”;类型可以统一;当它们派生自不同的基类时?,c#,generics,C#,Generics,我当前的非编译代码与此类似: 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 Handl
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) { }
}
公共抽象类A{}
公共类B{}
公共类C:A{}
公共接口IFoo
{
无效手柄(T项);
}
公共类MyFoo:IFoo,IFoo
TA:A在哪里
{
公共无效句柄(TA a){}
公共无效句柄(B){}
}
C#编译器拒绝编译此文件,引用以下规则/错误:
“MyProject.MyFoo”无法同时实现“MyProject.IFoo”和“MyProject.IFoo”,因为它们可能会统一用于某些类型参数替换
我理解这个错误的含义;如果TA
可以是任何东西,那么从技术上讲它也可以是aB
,这将在两个不同的句柄
实现上引入歧义
但是TA不可能是什么。基于类型层次结构,TA
不可能是aB
——至少,我认为它不可能TA
必须从A
派生,而A不是从B
派生的,显然C#/.NET中没有多类继承
如果我删除泛型参数并将TA
替换为C
,甚至A
,它将编译
那么为什么我会得到这个错误呢?这是编译器中的一个bug还是一般的非智能性,或者我还缺少什么
是否有任何解决方法,或者我只是需要将
MyFoo
泛型类重新实现为每个可能的TA
派生类型的一个单独的非泛型类?显然,它是按Microsoft Connect讨论的设计实现的:
public interface IIFoo<T> : IFoo<T>
{
}
公共接口IIFoo:IFoo
{
}
然后将其改为:
public class MyFoo<TA> : IIFoo<TA>, IFoo<B>
where TA : A
{
public void Handle(TA a) { }
public void Handle(B b) { }
}
公共类MyFoo:IIFoo,IFoo
TA:A在哪里
{
公共无效句柄(TA a){}
公共无效句柄(B){}
}
它现在编译得很好。现在猜猜看
难道不能在外部程序集中声明A、B和C吗?因为在编译MyFoo之后,类型层次结构可能会发生变化,从而给世界带来灾难
简单的解决方法就是实现Handle(A)而不是Handle(TA)(并使用IFoo而不是IFoo)。无论如何,使用Handle(TA)所做的事情都比不上从A访问方法(由于A:TA约束)
公共类MyFoo:IFoo,IFoo{
公共无效句柄(A){}
公共无效句柄(B){}
}
嗯,这个怎么样:
public class MyFoo<TA> : IFoo<TA>, IFoo<B>
where TA : A
{
void IFoo<TA>.Handle(TA a) { }
void IFoo<B>.Handle(B b) { }
}
公共类MyFoo:IFoo,IFoo
TA:A在哪里
{
void IFoo.Handle(TA a){}
void IFoo.Handle(B){}
}
这是C#4规范第13.4.2节的结果,其中规定: 如果从C创建的任何可能的构造类型在将类型参数替换为L后会导致L中的两个接口相同,则C的声明无效。在确定所有可能的构造类型时,不考虑约束声明 注意这里的第二句话 因此,它不是编译器中的错误;编译器是正确的。有人可能会说这是语言规范中的一个缺陷 一般来说,几乎在每种情况下都会忽略约束,在这种情况下,必须推导出关于泛型类型的事实。约束主要用于确定泛型类型参数的有效基类,其他很少 不幸的是,正如您所发现的,这有时会导致语言过于严格
通常,两次实现“同一”接口是一种不好的代码味道,在某种程度上仅通过泛型类型参数来区分。例如,有
类C:IEnumerable,IEnumerable
——C是什么?它既是一个海龟序列,又是一个长颈鹿序列?你能描述一下你在这里要做的事情吗?也许有更好的模式来解决真正的问题
如果您的界面与您描述的完全相同:
interface IFoo<T>
{
void Handle(T t);
}
及
类危险:IFoo,IFoo
{
void IFoo.Handle(iabcx){}
void IFoo.Handle(IDEF x){}
}
现在事情变得很疯狂
IFoo<IABCDEF> crazy = new Danger();
crazy.Handle(null);
IFoo疯狂=新的危险();
疯狂。句柄(null);
句柄的哪个实现被调用
有关此问题的更多想法,请参阅本文和评论:
请看我对基本相同问题的回答: 在某种程度上,这是可以做到的!我使用区分方法,而不是限定类型的限定符 它没有统一,事实上,它可能比它统一要好,因为您可以将单独的接口分离开来 请参阅我在这里的文章,在另一个上下文中提供了一个完全有效的示例。 基本上,您要做的是向IIndexer添加另一个类型参数,使其成为
IIndexer
然后,当你使用它两次时,你将“第一次”传递给第一次使用,“第二次”传递给第二次使用
因此,类测试变成:classTest:IIndexer,IIndexer
因此,您可以执行newtest()
其中,第一个和第二个是微不足道的:
interface First { }
interface Second { }
我知道帖子发布已经有一段时间了,但是对于那些通过搜索引擎来寻求帮助的人来说。请注意,“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 }
}
}
公共类MyFoo:IFoo其中TA:Base其中B:Base
{
公共无效句柄(基本对象)
{
如果(obj是TA){//TA特定代码或调用}
else如果(obj是B){//B特定代码或调用}
}
}
如果将一个接口放在一个基类上,则可以偷偷地将其隐藏起来
public interface IFoo<T>
{
}
public class Foo<T> : IFoo<T>
{
}
public class Foo<T1, T2> : Foo<T1>, IFoo<T2>
{
}
公共接口IFoo
{
}
公共类Foo:IFoo
{
}
公共类Foo:Foo,IFoo
{
}
我怀疑这是可行的,因为如果类型“统一”,则派生类的
IFoo<IABCDEF> crazy = new Danger();
crazy.Handle(null);
interface First { }
interface Second { }
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 }
}
}
public interface IFoo<T>
{
}
public class Foo<T> : IFoo<T>
{
}
public class Foo<T1, T2> : Foo<T1>, IFoo<T2>
{
}