9

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

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;

所以,至少我没有完全误解这一点。我不确定如何编写一个接受符合相同接口的通用类集合的方法。

如果需要,我有一个完全解决这个问题的计划,但我真的更愿意正确地完成它。

先感谢您。

4

3 回答 3

4

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

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

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

如果你不这样做,你可以这样做:

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

方法一:

将方法更改为:

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

方法二:

或者,将CallAllMethodsInContainer方法移动到MyContainer<T>类中,如下所示:

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

并将代码段更改为:

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

方法三:

编辑:另一种选择是从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列表将接受实现MyInterface.

于 2010-08-25T12:09:23.433 回答
3

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

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

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

这就是为什么你尝试的东西不可能的(取自我最近的回答):

考虑List<T>类型。假设你有 aList<string>和 a List<object>。string 派生自 object,但不遵循List<string>派生自List<object>; 如果是这样,那么你可以有这样的代码:

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

上面的代码说明了成为(而不是)协变类型的含义。请注意,如果是covariant ,则可以将类型转换为派生自的T<D>另一种类型(T<B>在.NET 4.0 中);如果泛型类型参数只以输出形式出现——即只读属性和函数返回值,则泛型类型是协变的。DBT

可以这样想:如果某种类型T<B>总是提供 aB,那么总是提供D( T<D>) 的类型将能够作为 a 操作,T<B>因为所有Ds 都是Bs。

顺便说一下,如果一个类型的泛型类型参数只以输入的形式出现——即方法参数,那么它就是逆变的。如果一个类型T<B>是逆变的,那么它可以被强制转换为 a T<D>,这可能看起来很奇怪。

可以这样想:如果某种类型T<B>总是需要 aB,那么它可以介入总是需要 a 的类型,D因为同样,所有Ds 都是Bs。

您的MyContainer类既不是协变的也不是逆变的,因为它的类型参数出现在两种上下文中——作为输入(通过Contents.Add)和作为输出(通过Contents属性本身)。

于 2010-08-25T12:13:30.213 回答
1

这是协方差问题

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

你不能投到MyContainer<MyClass>MyContainer<IMyInterface>因为那样你就可以做类似的事情Contents.Add(new AnotherClassThatImplementsIMyInterface())

于 2010-08-25T12:12:33.610 回答