7

由于IEnumerable在 C# 4.0 中有一个协变参数,我对它在以下代码中的行为感到困惑。

public class Test
{
    IEnumerable<IFoo> foos;

    public void DoTestOne<H>(IEnumerable<H> bars) where H : IFoo
    {
        foos = bars;
    }

    public void DoTestTwo(IEnumerable<IBar> bars)
    {
        foos = bars;
    }
}
public interface IFoo
{
}
public interface IBar : IFoo
{
}

所以基本上该DoTestOne方法在编译时不会编译DoTestTwo。除了为什么它不起作用之外,如果有人知道我如何实现DoTestOne(将 an 分配IEnumberable<H> where H : IFoo给 an IEnumberable<IFoo>)的效果,我将不胜感激。

4

4 回答 4

10

如果你知道 H 将是一个类,这确实有效:

    public void DoTestOne<H>(IEnumerable<H> bars) where H : class, IFoo
    {
        foos = bars;
    }

这里的问题是,如果 H 是值类型,则协方差并不完全符合您的期望,因为IEnumerable<MyStruct>实际上返回值类型而IEnumerable<IFoo>必须返回装箱实例。如有必要,您可以使用显式Cast<IFoo>来解决此问题。

于 2012-10-18T19:42:56.227 回答
3

你只需要一个演员就可以IEnumerable<IFoo>了:

public void DoTestOne<H>(IEnumerable<H> bars) where H : IFoo
{
    foos = (IEnumerable<IFoo>)bars;
}

编辑礼貌 Dan Bryant:使用foos = bars.Cast<IFoo>()而不是上面规避 InvalidCastException when His a struct.

于 2012-10-18T19:41:06.573 回答
1

在 .net 运行时中,每个值类型都有一个关联的同名堆对象类型。在某些情况下,将使用值类型;在其他情况下,堆类型。当声明值类型的存储位置(变量、参数、返回值、字段或数组槽)时,该存储位置将保存该类型的实际内容。当声明类类型的存储位置时,它将保存null或引用存储在别处的堆对象。接口类型的存储位置被视为引用类型的位置,并且即使接口的某些(或全部)实现实际上是值类型,也会保存堆引用。

尝试将值类型存储到引用类型存储位置将导致系统创建与值类型关联的堆类型的新实例,将所有字段从原始存储位置复制到新实例中的相应字段,并存储对该实例的引用,该过程称为“装箱”。尝试将堆引用转换为值类型存储位置将检查它是否引用与值类型关联的堆类型的实例;如果是这样,堆对象的字段将被复制(“取消装箱”)到值类型存储位置中的相应字段中。

尽管看起来像System.Int32派生自的类型System.Object,但这只是对了一半。有一个堆对象类型System.Int32,它确实派生自System.Object,但类型的变量System.Int32不包含对此类对象的引用。相反,这样的变量保存与该整数相关的实际数据;数据本身只是位的集合,并不来自任何东西

如果将接口类型的存储位置视为持有“从 System.Object 派生的实现接口_的东西”,那么实现该接口的任何类类型的实例都是该类型的实例,但值类型的实例 - 甚至如果它们可以转换为其他类型 - 不是任何其他类型的实例。使用 an 的代码IEnumerator<IFoo>不仅希望它的Current方法返回可以转换为 anIFoo或 implements的东西IFoo;它希望它返回的东西Objectthat implements的衍生物IFoo。因此,IEnumerable<T>要替换 a IEnumerable<IFoo>,有必要T同时约束两者来实现IFoo并且是 的适当导数System.Object

于 2012-10-18T22:22:43.047 回答
0

您忘记了退货中的演员表或通用约束中的“类”标识符。你所做的当然是可能的,参考下面。

来自:http: //msdn.microsoft.com/en-us/library/d5x73970.aspx

where T : <interface name>

The type argument must be or implement the specified interface.
Multiple interface constraints can be specified. The constraining interface can also be generic.
于 2012-10-18T19:44:50.877 回答