分配IEnumerable(协方差)
由于IEnumerable
在C#4.0中有一个协变参数,我很困惑它在以下代码中的行为方式。
public class Test { IEnumerable foos; public void DoTestOne(IEnumerable bars) where H : IFoo { foos = bars; } public void DoTestTwo(IEnumerable bars) { foos = bars; } } public interface IFoo { } public interface IBar : IFoo { }
所以基本上DoTestOne
方法不会在DoTestTwo
编译。 除了为什么它不起作用,如果有人知道我如何能够实现DoTestOne
的效果(指定一个IEnumberable where H : IFoo
到IEnumberable
),我将不胜感激。
如果你知道H将是一个类,这确实有效:
public void DoTestOne(IEnumerable bars) where H : class, IFoo { foos = bars; }
这里的问题是,如果H是值类型,协方差并不完全符合您的预期,因为IEnumerable
实际上返回值类型,而IEnumerable
必须返回盒装实例。 如有必要,您可以使用显式Cast
来解决此问题。
你只需要在那里使用IEnumerable
进行IEnumerable
:
public void DoTestOne(IEnumerable bars) where H : IFoo { foos = (IEnumerable)bars; }
编辑丹·布莱恩特:使用foos = bars.Cast
而不是上面当H
是struct
时绕过InvalidCastException。
您忘记了返回中的强制转换或通用约束中的“类”标识符。 你当然可以做什么,参考下面。
来自: http : //msdn.microsoft.com/en-us/library/d5x73970.aspx
where T : The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic.
在.net运行时中,每个值类型都具有相同名称的关联堆对象类型。 在某些情况下,将使用值类型; 在其他上下文中,堆类型。 声明值类型的存储位置(变量,参数,返回值,字段或数组槽)时,该存储位置将保存该类型的实际内容。 声明类类型的存储位置时,它将保留null
或对存储在其他位置的堆对象的引用。 接口类型的存储位置被视为类似于引用类型的存储位置,并且即使接口的某些(或所有)实现实际上是值类型,也保持堆引用。
尝试将值类型存储到引用类型存储位置将导致系统创建与值类型关联的堆类型的新实例,将原始存储位置中的所有字段复制到新实例中的相应字段,并存储对该实例的引用,称为“装箱”的过程。 尝试将堆引用强制转换为值类型存储位置将检查它是否引用与值类型关联的堆类型的实例; 如果是,则将堆对象的字段复制(“取消装箱”)到值类型存储位置中的相应字段。
虽然看起来像System.Int32
这样的类型派生自System.Object
,但这只是半真的。 有一个堆对象类型System.Int32
,它确实派生自System.Object
,但System.Int32
类型的变量不包含对这样一个对象的引用。 相反,这样的变量保存与该整数相关的实际数据; 数据本身只是一个比特集合,并不是来自任何东西 。
如果认为接口类型的存储位置为“Something派生自System.Object实现接口_ ”,则实现该接口的任何类类型的实例都是该类型的实例,但是值类型的实例 – 甚至如果它们可以转换为其他类型 – 不是任何其他类型的实例。 使用IEnumerator
代码不仅希望其Current
方法返回可以转换为IFoo
,还是实现IFoo
; 它希望它返回一些实现IFoo
的Object
衍生物。 因此,对于IEnumerable
替换IEnumerable
,必须将T
约束为实现IFoo
并且是System.Object
的适当派生。