简体   繁体   English

在c#中使用泛型时请帮助我理解多态性

[英]Please help me understand polymorphism when using generics in c#

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. 我有很多实现MyInterface的类。 I would like to write a method that can accept all MyContainer objects: 我想编写一个可以接受所有MyContainer对象的方法:

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? 当然,因为MyClass实现了IMyInterface,我应该能够实现它吗?

MyContainer<IMyInterface> newContainer = (MyContainer<IMyInterface>)container;

That didn't work either. 那也行不通。 I can definitely cast a normal MyClass to IMyInterface: 我绝对可以将一个普通的MyClass转换为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. 先感谢您。

Note: In all cases, you will have to initialize the Contents field to a concrete object that implements IList<?> 注意:在所有情况下,您必须将Contents字段初始化为实现IList<?>的具体对象

When you keep the generic constraint, you can do: 保留泛型约束时,可以执行以下操作:

public IList<T> Contents = new List<T>();

When you don't, you can do: 如果不这样做,您可以:

public IList<MyInterface> Contents = new List<MyInterface>();

Method 1: 方法1:

Change the method to: 将方法更改为:

public void CallAllMethodsInContainer<T>(MyContainer<T> container) where T : IMyInterface
{
    foreach (T myClass in container.Contents)
    {
        myClass.MyMethod();
    }
}

and the snippet to: 和片段:

MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);

Method 2: 方法2:

Alternatively, move the CallAllMethodsInContainer method to the MyContainer<T> class like this: 或者,将CallAllMethodsInContainer方法移动到MyContainer<T>类,如下所示:

public void CallAllMyMethodsInContents()
    {
        foreach (T myClass in Contents)
        {
            myClass.MyMethod();
        }
    }

and change the snippet to: 并将代码段更改为:

MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
container.CallAllMyMethodsInContents();

Method 3: 方法3:

EDIT: Yet another alternative is to remove the generic constraint from the MyContainer class like this: 编辑:另一种方法是从MyContainer类中删除通用约束,如下所示:

public class MyContainer
{
    public IList<MyInterface> Contents;
}

and to change the method signature to 并将方法签名更改为

  public void CallAllMethodsInContainer(MyContainer container)

Then the snippet should work as: 然后该片段应该如下:

MyContainer container = new MyContainer();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);

Note that with this alternative, the container's Contents list will accept any combination of objects that implement MyInterface . 请注意,使用此替代方法,容器的Contents列表将接受实现MyInterface任何对象组合。

Wow, this question's been coming up a lot lately. 哇,这个问题最近出现了很多。

Short answer: No, this isn't possible. 简短的回答:不,这是不可能的。 Here's what is possible: 可能的:

public void CallAllMethodsInContainer<T>(MyContainer<T> container) where T : IMyInterface
{
    foreach (IMyInterface myClass in container.Contents)
    {
        myClass.MyMethod();
    }
}

And here's why what you tried isn't possible (taken from this recent answer of mine ): 这就是为什么你尝试的不可能的(取自我最近的答案 ):

Consider the List<T> type. 考虑List<T>类型。 Say you have a List<string> and a List<object> . 假设您有一个List<string>和一个List<object> string derives from object, but it does not follow that List<string> derives from List<object> ; string派生自object,但它不遵循List<string>派生自List<object> ; if it did, then you could have code like this: 如果确实如此,那么你可以拥有这样的代码:

var strings = new List<string>();

// If this cast were possible...
var objects = (List<object>)strings;

// ...crap! then you could add a DateTime to a List<string>!
objects.Add(new DateTime(2010, 8, 23));23));

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 type T<B> where D derives from B is possible (in .NET 4.0) if T is covariant ; 注意,如果T协变的 ,则可以将类型T<D>为另一种类型T<B> ,其中DB派生(在.NET 4.0中); a generic type is covariant if its generic type argument only ever appears in the form of output -- ie, read-only properties and function return values. 泛型类型是协变的,如果它的泛型类型参数只以输出的形式出现 - 即只读属性和函数返回值。

Think of it this way: if some type T<B> always supplies a B , then one that always supplies a D ( T<D> ) will be able to operate as a T<B> since all D s are B s. 可以这样想:如果某种类型T<B>总是提供B ,那么总是提供DT<D> )的那个将能够作为T<B>因为所有D s都是B s。

Incidentally, a type is contravariant if its generic type parameter only ever appears in the form of input -- ie, method parameters. 顺便提一下,如果类型的泛型类型参数仅以输入的形式出现 - 即方法参数,则类型是逆变的 If a type T<B> is contravariant then it can be cast to a T<D> , as strange as that may seem. 如果类型T<B>是逆变的,则可以将其转换为T<D> ,这可能看起来很奇怪。

Think of it this way: if some type T<B> always requires a B , then it can step in for one that always requires a D since, again, all D s are B s. 可以这样想:如果某个类型T<B>总是需要B ,那么它可以介入一个总是需要D那个,因为所有的D都是B s。

Your MyContainer class is neither covariant nor contravariant because its type parameter appears in both contexts -- as input (via Contents.Add ) and as output (via the Contents property itself). 您的MyContainer类既不是协变也不是逆变,因为它的类型参数出现在两个上下文中 - 作为输入(通过Contents.Add )和作为输出(通过Contents属性本身)。

this is problem of covariance 这是协方差的问题

http://msdn.microsoft.com/en-us/library/dd799517.aspx http://msdn.microsoft.com/en-us/library/dd799517.aspx

you can't cast MyContainer<MyClass> to MyContainer<IMyInterface> because then you could do things like Contents.Add(new AnotherClassThatImplementsIMyInterface()) 你不能将MyContainer<MyClass>MyContainer<IMyInterface>因为那时你可以做一些像Contents.Add(new AnotherClassThatImplementsIMyInterface())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM