为什么不能IEnumerable的&LT;&结构GT;定投为IEnumerable&LT;对象&gt ;? [英] Why cannot IEnumerable<struct> be cast as IEnumerable<object>?
问题描述
为什么最后一行不被允许?
的IEnumerable&LT;双&GT; doubleenumerable =新的List&LT;双&GT; {1,2};
IEnumerable的&LT;串GT; stringenumerable =新的List&LT;串GT; {一,B};
IEnumerable的&LT;对象&gt; objects1 = stringenumerable; // 好
IEnumerable的&LT;对象&gt; objects2 = doubleenumerable; // 不允许
这是因为双是不会从对象中获得一个值类型,因此协方差不起作用?
这是否意味着没有办法,使这项工作:
公共接口IMyInterface的&LT;出T&GT;
{
字符串方法();
}公共类MyClass的&LT; U&GT; :IMyInterface的&LT; U&GT;
{
公共字符串的方法()
{
返回测试;
}
}公共类测试
{
公共静态对象test2的()
{
IMyInterface的&LT;双&GT;一个=新MyClass的&LT;双&GT;();
IMyInterface的&LT;对象&gt; B = A; //无效的转换!
返回弘();
}
}
和我需要写我自己的 IMyInterface的&LT; T&GT; .Cast&LT; U&GT;?()
来做到这一点。
为什么最后一行不被允许?
块引用>由于双是值类型和对象是引用类型;协方差只有当这两种类型是引用类型的作品。
这是因为双是不会从对象中获得一个值类型,因此协方差不起作用?
块引用>没有。双并从对象派生。所有的值类型派生自object。
现在的问题,你应该问:
为什么协方差不行转换
的IEnumerable&LT;双&GT;
到的IEnumerable&LT;对象&gt;
块引用>由于的谁做拳击的?从双A转换为对象必须的复选框的两倍。假设你有
的IEnumerator℃的调用;对象&gt; .Current
是真的,以实现在通话的IEnumerator&LT;双&GT; .Current
。呼叫者预计要返回的对象。被叫方返回一个double。 哪里是code,做拳击指令,变成由的IEnumerator&LT返回双;双&GT; .Current
成盒装双是无处的,这就是,这就是为什么这种转换是非法的。到
当前通话
是打算把一个八字节的两倍计算堆栈上,而消费者会期待一个四字节参考盒装双的评价栈,所以消费者会崩溃并与偏离的堆栈和无效的内存参考可怕的死亡。如果您希望code的箱的执行的话那一定是的写的在某些时候,你是谁可以写它的人。最简单的方法是使用
演员LT; T&GT;
扩展方法:的IEnumerable&LT;对象&gt; objects2 = doubleenumerable.Cast&LT;对象&gt;();
现在你调用包含转换的双重来自一个八字节双到参考拳击指令的helper方法。
更新:一个评论者指出,我乞求的问题 - 那就是,我已经回答了presupposing其中每一点很难解决了一个问题,因为原来的问题解决方案需要一个机制的存在的问题。如何实施
演员LT; T&GT;
管理解决是否知道框与否的问题。它是这样工作草图。请注意,参数类型的不的泛型:
公共静态的IEnumerable&LT; T&GT;铸造&LT; T&GT;(这个IEnumerable的序列)
{
如果(序列== NULL)抛出...
如果(序列的IEnumerable&LT; T&GT;)
返回序列为IEnumerable&LT; T&GT ;;
返回ReallyCast&LT; T&GT;(序列);
}私有静态的IEnumerable&LT; T&GT; ReallyCast&LT; T&GT;(IEnumerable的序列)
{
的foreach(在序列对象项)
产生回报(T)项目;
}用于确定从对象强制转换为T是否取消装箱转换或引用转换被推迟到运行时的责任。抖动知道T是否是引用类型或值类型。的99%的时间它会当然是引用类型。
Why is the last line not allowed?
IEnumerable<double> doubleenumerable = new List<double> { 1, 2 }; IEnumerable<string> stringenumerable = new List<string> { "a", "b" }; IEnumerable<object> objects1 = stringenumerable; // OK IEnumerable<object> objects2 = doubleenumerable; // Not allowed
Is this because double is a value type that doesn't derive from object, hence the covariance doesn't work?
Does that mean that there is no way to make this work:
public interface IMyInterface<out T> { string Method(); } public class MyClass<U> : IMyInterface<U> { public string Method() { return "test"; } } public class Test { public static object test2() { IMyInterface<double> a = new MyClass<double>(); IMyInterface<object> b = a; // Invalid cast! return b.Method(); } }
And that I need to write my very own
IMyInterface<T>.Cast<U>()
to do that?解决方案Why is the last line not allowed?
Because double is a value type and object is a reference type; covariance only works when both types are reference types.
Is this because double is a value type that doesn't derive from object, hence the covariance doesn't work?
No. Double does derive from object. All value types derive from object.
Now the question you should have asked:
Why does covariance not work to convert
IEnumerable<double>
toIEnumerable<object>
?Because who does the boxing? A conversion from double to object must box the double. Suppose you have a call to
IEnumerator<object>.Current
that is "really" a call to an implementation ofIEnumerator<double>.Current
. The caller expects an object to be returned. The callee returns a double. Where is the code that does the boxing instruction that turns the double returned byIEnumerator<double>.Current
into a boxed double?It is nowhere, that's where, and that's why this conversion is illegal. The call to
Current
is going to put an eight-byte double on the evaluation stack, and the consumer is going to expect a four-byte reference to a boxed double on the evaluation stack, and so the consumer is going to crash and die horribly with an misaligned stack and a reference to invalid memory.If you want the code that boxes to execute then it has to be written at some point, and you're the person who gets to write it. The easiest way is to use the
Cast<T>
extension method:IEnumerable<object> objects2 = doubleenumerable.Cast<object>();
Now you call a helper method that contains the boxing instruction that converts the double from an eight-byte double to a reference.
UPDATE: A commenter notes that I have begged the question -- that is, I have answered a question by presupposing the existence of a mechanism which solves a problem every bit as hard as a solution to the original question requires. How does the implementation of
Cast<T>
manage to solve the problem of knowing whether to box or not?It works like this sketch. Note that the parameter types are not generic:
public static IEnumerable<T> Cast<T>(this IEnumerable sequence) { if (sequence == null) throw ... if (sequence is IEnumerable<T>) return sequence as IEnumerable<T>; return ReallyCast<T>(sequence); } private static IEnumerable<T> ReallyCast<T>(IEnumerable sequence) { foreach(object item in sequence) yield return (T)item; }
The responsibility for determining whether the cast from object to T is an unboxing conversion or a reference conversion is deferred to the runtime. The jitter knows whether T is a reference type or a value type. 99% of the time it will of course be a reference type.
这篇关于为什么不能IEnumerable的&LT;&结构GT;定投为IEnumerable&LT;对象&gt ;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!