我正在尝试掌握.NET Generics的概念,并在我自己的代码中实际使用它们,但我一直遇到问题。
有人可以尝试向我解释为什么以下设置无法编译?
public class ClassA
{
ClassB b = new ClassB();
public void MethodA<T>(IRepo<T> repo) where T : ITypeEntity
{
b.MethodB(repo);
}
}
public class ClassB
{
IRepo<ITypeEntity> repo;
public void MethodB(IRepo<ITypeEntity> repo)
{
this.repo = repo;
}
}
我收到以下错误:
无法转换为IRepo&lt;'T&gt;到IRepo&lt;'ITypeEntity&gt;
使用IRepo&lt;'DetailType&gt;调用MethodA objectType,其中DetailType继承自ITypeEntity。
我一直认为这应该编译,因为我将MethodA中的T限制为ITypeEntity类型。
任何想法或反馈都会非常有帮助。
感谢。
编辑: Nick R有一个很好的建议但不幸的是在我的背景下,我没有选择制作ClassA Generic。但是,ClassB可能就是这样。
答案 0 :(得分:3)
好吧这个编译好了。我基本上重新定义了类来获取泛型参数。这可能适合你的情况。
public interface IRepo<TRepo>
{
}
public interface ITypeEntity
{
}
public class ClassA<T> where T : ITypeEntity
{
ClassB<T> b = new ClassB<T>();
public void MethodA(IRepo<T> repo)
{
b.MethodB(repo);
}
}
public class ClassB<T> where T : ITypeEntity
{
IRepo<T> repo;
public void MethodB(IRepo<T> repo)
{
this.repo = repo;
}
}
答案 1 :(得分:3)
使用泛型时,继承不起作用。正如Smashery指出的那样,即使TypeA继承自TypeB,myType&lt; TypeA&gt;不会从myType&lt; TypeB&gt;继承。
因此,您无法调用定义为MethodA(myType&lt; TypeB&gt; b)的方法,期望myType&lt; TypeB&gt;并给它一个myType&lt; TypeA&gt;代替。有问题的类型必须完全匹配。因此,以下内容将无法编译:
myType<TypeA> a; // This should be a myType<TypeB>, even if it contains only TypeA's
public void MethodB(myType<TypeB> b){ /* do stuff */ }
public void Main()
{
MethodB(a);
}
因此,在您的情况下,您需要传入IRepo&lt; ITypeEntity&gt;到MethodB,即使它只包含DetailTypes。您需要在两者之间进行一些转换。如果您使用的是通用IList,则可以执行以下操作:
public void MethodA<T>(IList<T> list) where T : ITypeEntity
{
IList<T> myIList = new List<T>();
foreach(T item in list)
{
myIList.Add(item);
}
b.MethodB(myIList);
}
我希望这有用。
答案 2 :(得分:2)
问题是一个棘手的问题。 DetailType可以从ITypeEntity继承,但实际上不是ITypeEntity。您对DetailType的实现可能会引入不同的功能,因此DetailType实现了ITypeEntity但不等于ITypeEntity。我希望这是有道理的......
答案 3 :(得分:1)
我收到以下错误:不能 从IRepo&lt;'T&gt;转换至 IRepo&LT;'ITypeEntity&GT;
您收到此编译错误,因为IRepo<T>
和IRepo<ITypeEntity>
不是相同的事情。后者是前者的专业化。 IRepo<T>
是泛型类型定义,其中类型参数T是占位符,IRepo<ITypeEntity>
是泛型类型定义的构造泛型类型 ,其中类型参数T from指定为ITypeEntity
。
我一直认为这应该 编译,因为我限制T内 方法A属于ITypeEntity类型。
where
约束在此处没有帮助,因为它只会限制您在MethodA
的呼叫网站上为T提供的类型。
以下是MSDN文档中的术语(请参阅Generics in the .NET Framework),这可能有所帮助:
泛型类型定义是a
类,结构或接口
声明作为一个
模板,带占位符
它可以包含或使用的类型。
例如,Dictionary<<K, V>
类可以包含
两种类型:键和值。因为
它只是一个模板,你不能
创建一个类的实例,
结构,或接口是一个
泛型定义。
通用类型参数或类型
参数,是占位符
泛型类型或方法定义。
Dictionary<K, V>
泛型类型有两种类型
参数,K和V,即
表示其键的类型和
值。
构造的通用类型,或 构造类型,是结果 指定泛型的类型 类型参数的泛型类型 定义。
泛型类型参数是任何类型 用于替代泛型 类型参数。
通用术语泛型 包括构造类型和 泛型类型定义。
限制是限制的
泛型类型参数。对于
例如,您可以限制类型
实现的类型的参数
IComparer<T>
通用
接口,以确保实例
可以订购该类型。您可以
也限制类型参数
具有特定基础的类型
class,有默认值
构造函数,或者是引用
类型或值类型。用户的
泛型类型不能替代类型
不满足的论据
约束
答案 4 :(得分:1)
请参阅@monoxide的问题
而as I said there,查看Eric Lippert关于仿制药的逆变和协方差的一系列帖子将使这一点更加清晰。
答案 5 :(得分:0)
如果B是A的子类,那并不意味着Class<B>
是Class<A>
的子类。因此,出于同样的原因,如果您说“T
是ITypeEntity
”,那并不意味着“IRepo<T>
是IRepo<ITypeEntity>
”。如果你想让它工作,你可能必须编写自己的转换方法。
答案 6 :(得分:0)
T是一个类型变量,它将被绑定到使用中的部分类型。该限制确保该类型将代表实现ITypeEntity的类型的子集,不包括实现该接口的其他类型。
答案 7 :(得分:0)
在编译时,即使您对其进行约束,编译器也只知道MethodA中的T是引用类型。它不知道它受限制的类型。
答案 8 :(得分:0)
这是泛型的冗余使用,如果T只能是ITypeEntity的一个实例,则不应使用泛型。
泛型是指当你有多种类型的东西时。
答案 9 :(得分:0)
在围绕泛型方法的过程中,请允许我给你一个简单的泛型函数。它是VB的IIf()(立即if)的通用等价物,它本身就是对C风格的三元运算符(?)的模仿。它对任何事情都没有用,因为真正的三元运算符更好,但它可能会帮助你理解泛型函数的构建方式以及它们应该应用于何种上下文。
T IIF<T>(bool Expression, T TruePart, T FalsePart)
{
return Expression ? TruePart : FalsePart;
}