Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/299.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么不能使用IEnumerable<;结构>;被选为IEnumerable<;对象>;?_C#_Generics_Covariance - Fatal编程技术网

C# 为什么不能使用IEnumerable<;结构>;被选为IEnumerable<;对象>;?

C# 为什么不能使用IEnumerable<;结构>;被选为IEnumerable<;对象>;?,c#,generics,covariance,C#,Generics,Covariance,为什么最后一行是不允许的 IEnumerable<double> doubleenumerable = new List<double> { 1, 2 }; IEnumerable<string> stringenumerable = new List<string> { "a", "b" }; IEnumerable<object> objects1 = stringenumerable; // OK IEnumerable<o

为什么最后一行是不允许的

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
IEnumerable doubleenumerable=新列表{1,2};
IEnumerable stringenumerable=新列表{“a”,“b”};
IEnumerable objects1=stringenumerable;//好啊
IEnumerable objects2=doubleenumerable;//不准
这是因为double是一种值类型,它不是从object派生的,因此协方差不起作用吗

这是否意味着无法实现这一目标:

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
{
字符串方法();
}
公共类MyClass:IMyInterface
{
公共字符串方法()
{
返回“测试”;
}
}
公开课考试
{
公共静态对象test2()
{
IMyInterface a=新的MyClass();
IMyInterface b=a;//强制转换无效!
返回b.Method();
}
}
我需要编写自己的
IMyInterface.Cast()
才能做到这一点

为什么最后一行是不允许的

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是值类型,object是引用类型;协方差仅在两种类型都是引用类型时有效

这是因为double是一种值类型,它不是从object派生的,因此协方差不起作用吗

不。Double确实是从object派生的。所有值类型都派生自对象

现在你应该问的问题是:

为什么协方差不能将
IEnumerable
转换为
IEnumerable

因为谁打拳击?从double到object的转换必须包含double。假设您有一个对
IEnumerator.Current
的调用,这“实际上”是对
IEnumerator.Current
实现的调用。调用方希望返回一个对象。被叫方返回一个double执行装箱指令的代码在哪里,该指令将IEnumerator.Current返回的双精度码转换为装箱双精度码?

它不在任何地方,这就是为什么这种转换是非法的。调用
Current
将在求值堆栈上放置一个8字节的双精度浮点值,并且使用者将期望在求值堆栈上对装箱双精度浮点值进行一个4字节的引用,因此使用者将崩溃,并在堆栈未对齐和对无效内存的引用的情况下可怕地死去

如果你想让框中的代码执行,那么它必须在某个时候被编写,而你就是编写它的人。最简单的方法是使用
Cast
扩展方法:

IEnumerable<object> objects2 = doubleenumerable.Cast<object>();
IEnumerable objects2=doubleenumerable.Cast();
现在调用一个helper方法,该方法包含将double从八字节double转换为引用的装箱指令

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

它就像这张草图。请注意,参数类型不是泛型的:

公共静态IEnumerable强制转换(此IEnumerable序列)
{
如果(序列==null)抛出。。。
if(序列是IEnumerable)
返回序列为IEnumerable;
返回ReallyCast(序列);
}
私有静态IEnumerable ReallyCast(IEnumerable序列)
{
foreach(顺序中的对象项)
收益回报(T)项;
}

确定从对象到T的转换是取消装箱转换还是引用转换的责任推迟到运行时。抖动知道T是引用类型还是值类型。99%的时间它当然是一个引用类型。

此类型的变体仅支持引用类型。参见

要了解什么是允许的,什么是不允许的,以及为什么事情会这样,了解引擎盖下发生了什么是很有帮助的。对于每个值类型,都存在一个对应的类对象类型,与所有对象一样,它将从
System.object
继承。每个类对象的数据都包含一个32位字(x86)或64位长字(x64),用于标识其类型。但是,值类型存储位置不保存此类类对象或对它们的引用,也不存储类型数据字。相反,每个原语值类型位置仅保存表示值所需的位,每个结构值类型存储位置仅保存该类型的所有公共和私有字段的内容

将类型为
Double
的变量复制到类型为
Object
的变量时,将创建与
Double
关联的类对象类型的新实例,并将所有字节从原始类对象复制到新类对象。尽管装箱的-
Double
类类型与
Double
值类型具有相同的名称,但这不会导致歧义,因为它们通常不能在相同的上下文中使用。值类型的存储位置保存原始位或字段组合,没有存储的类型信息;将一个这样的存储位置复制到另一个位置会复制所有字节,从而复制所有公共和私有字段。相反,从值类型派生的类型的堆对象是堆对象,其行为类似于堆对象。尽管C#将值类型存储位置的内容视为
对象的派生,但在后台,此类存储位置的内容只是字节的集合,实际上是类型系统之外的内容。由于它们只能由知道字节代表什么的代码访问,因此不需要将此类信息存储在存储位置本身。虽然在结构上调用
GetType
时装箱的必要性通常用
GetType
作为一个无阴影、非虚函数来描述,但是