请帮助我理解在 C# 中使用泛型时的多态性
我在理解使用泛型时多态性如何工作时遇到问题。作为一个例子,我定义了以下程序:
public interface IMyInterface
{
void MyMethod();
}
public class MyClass : IMyInterface
{
public void MyMethod()
{
}
}
public class MyContainer<T> where T : IMyInterface
{
public IList<T> Contents;
}
然后我可以这样做,效果很好:
MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
我有许多实现 MyInterface 的类。我想编写一个可以接受所有 MyContainer 对象的方法:
public void CallAllMethodsInContainer(MyContainer<IMyInterface> container)
{
foreach (IMyInterface myClass in container.Contents)
{
myClass.MyMethod();
}
}
现在,我想调用这个方法。
MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);
那行不通。当然,因为 MyClass 实现了 IMyInterface,所以我应该能够强制转换它?
MyContainer<IMyInterface> newContainer = (MyContainer<IMyInterface>)container;
那也没用。我绝对可以将普通的 MyClass 转换为 IMyInterface:
MyClass newClass = new MyClass();
IMyInterface myInterface = (IMyInterface)newClass;
所以,至少我没有完全误解这一点。我不确定如何编写一个方法来接受符合同一接口的类的通用集合。
如果需要的话,我有一个计划来完全解决这个问题,但我真的更愿意正确地做到这一点。
先感谢您。
I am having a problem understanding how polymorphism works when using generics. As an example, I have defined the following program:
public interface IMyInterface
{
void MyMethod();
}
public class MyClass : IMyInterface
{
public void MyMethod()
{
}
}
public class MyContainer<T> where T : IMyInterface
{
public IList<T> Contents;
}
I can then do this, which works just fine:
MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
I have many classes that implement MyInterface. I would like to write a method that can accept all MyContainer objects:
public void CallAllMethodsInContainer(MyContainer<IMyInterface> container)
{
foreach (IMyInterface myClass in container.Contents)
{
myClass.MyMethod();
}
}
Now, I'd like to call this method.
MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);
That didn't work. Surely, because MyClass implements IMyInterface, I should be able to just cast it?
MyContainer<IMyInterface> newContainer = (MyContainer<IMyInterface>)container;
That didn't work either. I can definitely cast a normal MyClass to IMyInterface:
MyClass newClass = new MyClass();
IMyInterface myInterface = (IMyInterface)newClass;
So, at least I haven't completely misunderstood that. I am unsure exactly how I am to write a method that accepts a generic collection of classes that conform to the same interface.
I have a plan to completely hack around this problem if need be, but I would really prefer to do it properly.
Thank you in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
注意:在所有情况下,您都必须将
Contents
字段初始化为实现IList
的具体对象。当您保留通用约束时,您可以执行以下操作:
如果不这样做,您可以执行以下操作:
方法 1:
将方法更改为:
并将代码段更改为:
方法 2:
或者,移动
CallAllMethodsInContainer 方法到
MyContainer
类,如下所示:并将代码片段更改为:
方法 3:
编辑:另一种选择是从 < code>MyContainer 类如下所示:
并将方法签名更改为
然后代码片段应按以下方式工作:
请注意,使用此替代方案,容器的
Contents
列表将接受实现的对象的任意组合我的界面
。Note: In all cases, you will have to initialize the
Contents
field to a concrete object that implementsIList<?>
When you keep the generic constraint, you can do:
When you don't, you can do:
Method 1:
Change the method to:
and the snippet to:
Method 2:
Alternatively, move the
CallAllMethodsInContainer
method to theMyContainer<T>
class like this:and change the snippet to:
Method 3:
EDIT: Yet another alternative is to remove the generic constraint from the
MyContainer
class like this:and to change the method signature to
Then the snippet should work as:
Note that with this alternative, the container's
Contents
list will accept any combination of objects that implementMyInterface
.哇,这个问题最近出现很多。
简短的回答:不,这是不可能的。这就是可能的可能性:
这就是为什么您尝试的不可能可能的原因(摘自我最近的回答):
考虑
List
类型。假设您有一个List
和一个List
上面的代码说明了成为(和不成为) 协变类型。请注意,将类型
T
转换为另一个类型T
(其中D
派生自B
)是如果T
是协变,则可能(在 .NET 4.0 中);如果泛型类型参数仅以输出形式出现(即只读属性和函数返回值),则泛型类型是协变的。可以这样想:如果某种类型
T
总是提供B
,那么总是提供D< /code> (
T
) 将能够作为T
运行,因为所有D
都是B
。顺便说一句,如果一个类型的泛型类型参数仅以输入(即方法参数)的形式出现,那么该类型就是逆变的。如果类型
T
是逆变的,那么它可以转换为T
,尽管这看起来很奇怪。可以这样想:如果某种类型
T
总是需要B
,那么它可以介入始终需要B
的类型D
因为所有D
都是B
。您的
MyContainer
类既不是协变也不是逆变,因为它的类型参数出现在两个上下文中——作为输入(通过Contents.Add
)和输出(通过Contents
属性本身)。Wow, this question's been coming up a lot lately.
Short answer: No, this isn't possible. Here's what is possible:
And here's why what you tried isn't possible (taken from this recent answer of mine):
Consider the
List<T>
type. Say you have aList<string>
and aList<object>
. string derives from object, but it does not follow thatList<string>
derives fromList<object>
; if it did, then you could have code like this:The above code illustrates what it means to be (and not to be) a covariant type. Note that casting a type
T<D>
to another typeT<B>
whereD
derives fromB
is possible (in .NET 4.0) ifT
is covariant; a generic type is covariant if its generic type argument only ever appears in the form of output -- i.e., read-only properties and function return values.Think of it this way: if some type
T<B>
always supplies aB
, then one that always supplies aD
(T<D>
) will be able to operate as aT<B>
since allD
s areB
s.Incidentally, a type is contravariant if its generic type parameter only ever appears in the form of input -- i.e., method parameters. If a type
T<B>
is contravariant then it can be cast to aT<D>
, as strange as that may seem.Think of it this way: if some type
T<B>
always requires aB
, then it can step in for one that always requires aD
since, again, allD
s areB
s.Your
MyContainer
class is neither covariant nor contravariant because its type parameter appears in both contexts -- as input (viaContents.Add
) and as output (via theContents
property itself).这是协方差问题
http://msdn.microsoft.com/en-us /library/dd799517.aspx
您无法将
MyContainer
转换为MyContainer
因为这样您就可以执行诸如Contents.添加(新的 AnotherClassThatImplementsIMyInterface())
this is problem of covariance
http://msdn.microsoft.com/en-us/library/dd799517.aspx
you can't cast
MyContainer<MyClass>
toMyContainer<IMyInterface>
because then you could do things likeContents.Add(new AnotherClassThatImplementsIMyInterface())