本文介绍了为什么不能 IEnumerable&lt;struct&gt;被强制转换为 IEnumerable<object>?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么不允许最后一行?

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

这是不是因为 double 是不是从对象派生的值类型,因此协方差不起作用?

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();
    }
}

而且我需要编写我自己的 IMyInterface.Cast() 来做到这一点?

And that I need to write my very own IMyInterface<T>.Cast<U>() to do that?

推荐答案

因为double是值类型而object是引用类型;协方差仅在两种类型都是引用类型时有效.

Because double is a value type and object is a reference type; covariance only works when both types are reference types.

这是不是因为 double 是不是从对象派生的值类型,因此协方差不起作用?

没有.Double 确实从对象派生.所有值类型都派生自对象.

No. Double does derive from object. All value types derive from object.

现在你应该问的问题:

为什么协方差无法将 IEnumerable 转换为 IEnumerable?

因为谁打拳击?从 double 到 object 的转换必须box double.假设您有一个对 IEnumerator.Current 的调用,它实际上"是对 IEnumerator.Current 实现的调用.调用者期望返回一个对象.被调用者返回一个双精度值.IEnumerator<double>.Current返回的double变成装箱的double的装箱指令的代码在哪里?

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?

无处,那是哪里,这就是为什么这种转换是非法的.对 Current 的调用将在评估堆栈上放置一个八字节的双精度值,并且消费者将期望对评估堆栈上的装箱双精度值的四字节引用,因此消费者将因堆栈未对齐和对无效内存的引用而崩溃和死亡.

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.

如果您希望框内的代码执行,那么它必须在某个时候编写,而您就是编写它的人.最简单的方法是使用Cast扩展方法:

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.

更新:一位评论者指出我提出了这个问题——也就是说,我通过假设存在一种机制来回答一个问题,该机制可以像解决原始问题一样困难地解决问题.Cast的实现是如何解决知道是否装箱的问题的?

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;
}

确定从对象到 T 的转换是拆箱转换还是引用转换的责任推迟到运行时.抖动知道 T 是引用类型还是值类型.99% 的情况下它当然是引用类型.

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;struct&gt;被强制转换为 IEnumerable<object>?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 13:08