为什么不能IEnumerable的<&结构GT;定投为IEnumerable<对象&gt ;? [英] Why cannot IEnumerable<struct> be cast as IEnumerable<object>?

查看:167
本文介绍了为什么不能IEnumerable的<&结构GT;定投为IEnumerable<对象&gt ;?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么最后一行不被允许?

 的IEnumerable<双> doubleenumerable =新的List<双> {1,2};
IEnumerable的<串GT; stringenumerable =新的List<串GT; {一,B};
IEnumerable的<对象> objects1 = stringenumerable; // 好
IEnumerable的<对象> objects2 = doubleenumerable; // 不允许

这是因为双是不会从对象中获得一个值类型,因此协方差不起作用?

这是否意味着没有办法,使这项工作:

 公共接口IMyInterface的<出T>
{
    字符串方法();
}公共类MyClass的< U> :IMyInterface的< U>
{
    公共字符串的方法()
    {
        返回测试;
    }
}公共类测试
{
    公共静态对象test2的()
    {
        IMyInterface的<双>一个=新MyClass的<双>();
        IMyInterface的<对象> B = A; //无效的转换!
        返回弘();
    }
}

和我需要写我自己的 IMyInterface的< T> .Cast< U>?()来做到这一点。


解决方案

  

为什么最后一行不被允许?


由于双是值类型和对象是引用类型;协方差只有当这两种类型是引用类型的作品。


  

这是因为双是不会从对象中获得一个值类型,因此协方差不起作用?


没有。双并从对象派生。所有的值类型派生自object。

现在的问题,你应该问:


  

为什么协方差不行转换的IEnumerable<双> 的IEnumerable<对象>


由于的谁做拳击的?从双A转换为对象必须的复选框的两倍。假设你有的IEnumerator℃的调用;对象> .Current 是真的,以实现在通话的IEnumerator<双> .Current 。呼叫者预计要返回的对象。被叫方返回一个double。 哪里是code,做拳击指令,变成由的IEnumerator&LT返回双;双> .Current 成盒装双

无处的,这就是,这就是为什么这种转换是非法的。到当前通话是打算把一个八字节的两倍计算堆栈上,而消费者会期待一个四字节参考盒装双的评价栈,所以消费者会崩溃并与偏离的堆栈和无效的内存参考可怕的死亡。

如果您希望code的箱的执行的话那一定是的的在某些时候,你是谁可以写它的人。最简单的方法是使用演员LT; T> 扩展方法:

 的IEnumerable<对象> objects2 = doubleenumerable.Cast<对象>();

现在你调用包含转换的双重来自一个八字节双到参考拳击指令的helper方法。

更新:一个评论者指出,我乞求的问题 - 那就是,我已经回答了presupposing其中每一点很难解决了一个问题,因为原来的问题解决方案需要一个机制的存在的问题。如何实施演员LT; T> 管理​​解决是否知道框与否的问题。

它是这样工作草图。请注意,参数类型的的泛型:

 公共静态的IEnumerable< T>铸造< T>(这个IEnumerable的序列)
{
    如果(序列== NULL)抛出...
    如果(序列的IEnumerable< T>)
        返回序列为IEnumerable< T&GT ;;
    返回ReallyCast< T>(序列);
}私有静态的IEnumerable< T> ReallyCast< T>(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> to IEnumerable<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 of IEnumerator<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 by IEnumerator<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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆