根据这个MSDN reference IEnumerable
是协变的,这可以隐式地将对象列表强制转换为可枚举的对象:
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
在我自己的代码中,我编写了一行代码,当列表的项类型为类Point
时,它是完美的(Point是一个简单的类,有三个属性double x,y,z):
var objects = (IEnumerable<object>)dataModel.Value;
// here property Value is a list that could be of any type.
但是当列表的项类型为double
时,上面的代码会返回以下异常:
Unable to cast object of type System.Collections.Generic.List1[System.Double]
to type System.Collections.Generic.IEnumerable1[System.Object].
string
和double
之间的区别是什么导致代码与string
一起使用而不是与double
一起使用?
根据this post我们可以简单地将列表转换为IEnumerable
(没有类型参数),因此我只需要遍历项目并将新项目添加到列表中(实际上我不需要根本列出的列表项目。我决定使用这个:
var objects = (IEnumerable)dataModel.Value;
但是如果您需要将列表中的项目投射到object
并使用它们,那么Theodoros的答案就是您最常遵循的解决方案。
答案 0 :(得分:21)
变体接口的构造类型(例如IEnumerable<out T>
)仅对于引用类型参数是变体,因为它们对超类型(例如object
)的隐式转换是非表示变化的转换。这就是IEnumerable<string>
协变的原因。
值类型参数的构造类型是不变的,因为由于the boxing that's required,它们对超类型(例如object
)的隐式转换是表示更改的。这就是IEnumerable<double>
不变的原因。
差异仅适用于参考类型;如果为变量类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。
可能的解决方法是使用LINQ强制转换:
var sequence = list.Cast<object>();
这将首先检查源是否与您尝试将其转换为IEnumerable<TResult>
的分配兼容。
T
为string
的情况),it will return the initial source directly(这是理想的情况,在性能方面)。T
double
try:
# operation which causes AttributeError
except AttributeError:
continue
就知道了这种情况),it will create a projection that attempts the conversion manually for each individual element。