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

发布于 2024-09-16 12:46:39 字数 1388 浏览 11 评论 0原文

我在理解使用泛型时多态性如何工作时遇到问题。作为一个例子,我定义了以下程序:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

戈亓 2024-09-23 12:46:39

注意:在所有情况下,您都必须将 Contents 字段初始化为实现 IList 的具体对象。

当您保留通用约束时,您可以执行以下操作:

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

如果不这样做,您可以执行以下操作:

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

方法 1:

将方法更改为:

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

并将代码段更改为:

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

方法 2:

或者,移动 CallAllMethodsInContainer 方法到 MyContainer 类,如下所示:

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

并将代码片段更改为:

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

方法 3:

编辑:另一种选择是从 < code>MyContainer 类如下所示:

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

并将方法签名更改为

  public void CallAllMethodsInContainer(MyContainer container)

然后代码片段应按以下方式工作:

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

请注意,使用此替代方案,容器的 Contents 列表将接受实现的对象的任意组合我的界面

Note: In all cases, you will have to initialize the Contents field to a concrete object that implements 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:

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:

Alternatively, move the CallAllMethodsInContainer method to the MyContainer<T> class like this:

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:

EDIT: Yet another alternative is to remove the generic constraint from the MyContainer class like this:

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.

平生欢 2024-09-23 12:46:39

哇,这个问题最近出现很多。

简短的回答:不,这是不可能的。这就是可能的可能性:

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

这就是为什么您尝试的不可能可能的原因(摘自我最近的回答):

考虑 List 类型。假设您有一个 List 和一个 List。 string 派生自 object,但并不意味着 List 派生自 List;如果是这样,那么您可以使用如下代码:

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));

上面的代码说明了成为(和不成为) 协变类型。请注意,将类型 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:

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. Say you have a List<string> and a List<object>. string derives from object, but it does not follow that List<string> derives from 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; 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 a B, then one that always supplies a D (T<D>) will be able to operate as a T<B> since all Ds are Bs.

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 a T<D>, as strange as that may seem.

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 Ds are Bs.

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).

如果没有你 2024-09-23 12:46:39

这是协方差问题

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> to MyContainer<IMyInterface> because then you could do things like Contents.Add(new AnotherClassThatImplementsIMyInterface())

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文