从书中:
1) int i = 7;
2) Object o = i; // Implicit boxing int-->Object
3) Object[] a3 = new int[] { 1, 2 }; // Illegal: no array conversion
Run Code Online (Sandbox Code Playgroud)
3)中的赋值是非法的,因为int不是引用类型,因此int []不能隐式转换为Object []
我不懂.在第2行)它显示int可以隐式转换为Object,在第三行中,它表示int []不可隐式转换.哇?
Ada*_*son 22
不重述问题,但这是因为int[]并非隐含地(或明确地,可转换为)转换为Object[].
数组本身就是类型.int[]简单地表示"一系列int变量",就像Object[]"一系列Object变量"一样.
顺便提一下,所有数组都是引用类型,它只是int[] 表示一系列特定值类型的引用类型.
编辑
不要让关于协方差的讨论混淆你.这是一个被广泛误解的话题,对于初学者试图解决的问题并不是真正有益的话题.是的,.NET 4.0引入了将操作指定为协变或逆变的能力,但是不允许在不兼容类型之间进行赋值兼容,例如Object[]和int[].考虑以下示例,假设它将编译.
int[] ints = new int[] { 1, 2, 3 };
Object[] objects = ints;
objects[1] = "hello";
int foo = ints[1];
Run Code Online (Sandbox Code Playgroud)
现在我们遇到了问题.如果将a分配给a是合法int[]的Object[],那么我现在突然(神奇地)string在我的int[]数组中有一个值- 这种情况应该永远不会发生,实际上不可能发生,因为int变量不能保存string值.
其他人(正确地)指出这样的东西会编译:
string[] strings = new string[] { "a", "b", "c" };
Object[] objects = strings;
objects[1] = 4;
Run Code Online (Sandbox Code Playgroud)
我会把它留给像Eric Lippert这样的人解释为什么这种操作有效(它基本上假定协方差时不一定是这样)[ 编辑: 感谢蒂姆古德曼,他实际上发布了埃里克的解释,或至少声明关于这个 ],但从根本上说,任何引用类型在技术上都能够保存对任何类型的引用.换句话说,当我声明一个string变量时,它会分配相同数量的内存(对于变量),就像我要声明一个DbConnection变量一样; 他们都是参考类型.对于值类型,分配的内存量取决于类型,并且它们基本上不兼容.
但是,您将注意到,在执行最后一步(分配给第二个数组元素)时,您将获得运行时异常(),因为底层数组实际上是a .ArrayTypeMismatchExceptionintstring[]
Eri*_*ert 13
显然这是一个令人困惑的问题.不幸的是,蒂姆古德曼的答案,我认为是最明确和正确地解决实际陈述问题的答案,被低估并删除.李的答案也很好地打破了这个标志.
综上所述,让我将问题重新解释为一些更精确的问题.
什么是"协方差",因为它适用于分配兼容性?
简而言之:考虑从一种类型到另一种类型的映射.再说了,int --> int[],string --> string[]等.也就是说,映射"x映射到x的数组".如果该映射保留了赋值兼容性,则它是协变映射.简而言之,我们说"数组是协变的",意思是"数组赋值兼容性规则与其元素类型的赋值兼容性规则相同,因为从元素类型到数组类型的映射是协变的".
有关协方差和赋值兼容性之间关系的更多信息,请参阅:
数组在C#中实际上是协变的吗?
仅当元素类型是引用类型时,它们才是协变的.
那种协方差类型是否安全?
不,它坏了.它缺乏类型安全性意味着在编译时成功的操作可能会在运行时抛出异常.这也意味着每个可能导致此类异常的数组赋值都需要检查是否应该抛出异常,这很昂贵.基本上我们这里有一个危险的,昂贵的功能,你不能选择退出.我对此并不特别高兴,但这就是我们所坚持的.
有关这种破坏的协方差形式的更多详细信息,请参见http://blogs.msdn.com/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx.
CLR是否支持值类型数组的协方差?
是.看到
我从答案中写到:
C#是否支持安全的协方差形式?
从C#4开始,是的.我们支持使用引用类型参数化的通用接口和委托的协方差和逆变.C#3不支持通用方差.
例如,在C#4中,IEnumerable<string>可以将实现的对象分配给类型的变量IEnumerable<object>.
请参阅http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
有关此功能的扩展讨论.
现在我们已经完成了预备工作,我们可以提出您的实际问题:
为什么泛型和数组协方差仅适用于引用类型,而不适用于值类型?
因为一些转换是表示保留的,一些是表示改变的.
当你说
string x = "hello";
object y = x;
Run Code Online (Sandbox Code Playgroud)
存储y 的内容与存储x的内容完全相同.它们都是位模式,意味着"在GC堆上引用此对象".无论CLR是将这些位解释为对象的引用还是对字符串的引用,该位模式都是相同的.
数组只不过是一大堆存储.当您重新解释string []的内容作为对象的引用时,其位中的任何内容都不得更改.它们保持完全相同,CLR认为它们是对象,而不是特定的字符串.你只要看看有趣的字符串及其对象.
将int转换为对象时,我们会分配一个框以将int放入.int是一个32位整数,包含自己的值.该框表示为GC堆中的32或64位管理地址,然后包含32位int值.那些是完全不同的位!将对象转换为int需要大量的工作.你不能只看看有趣的嘿,它看起来像一个对象.你必须分配内存才能使它工作.
这就是为什么你不能将一个int数组转换为一个对象数组.十个整数的数组可能占用320位,每个位都是整数本身的一部分.一个由十个对象组成的数组是GC堆上的 320或640位托管地址,谁将完成所有这些分配?我们希望参考转换快速而便宜 ; 这种转换要求我们基本上分配一个完整的新数组并复制整个内容; 结果将不再具有与原始数组的引用标识,因此对它的更改将丢失.或者,我们必须编写更多代码,这些代码对新数组进行了更改,并将它们编组回原来的数组.
看到
http://ericlippert.com/2009/03/03/representation-and-identity/
有关表示保留转换的更多讨论.
这是否回答你的问题?我知道这是一个令人困惑的话题.它深入研究了CLR的基本设计决策.
这是因为int是一种值类型.这种转换对于参考类型来说是合法的.
根据C#编译器团队的Eric Lippert的说法:
C#的数组协方差规则是"如果X是隐式可转换为引用类型Y的引用类型,则X []可隐式转换为Y []"
提问者问为什么这与int可转换为对象的事实形成对比.请注意,int继承自object,但int []不继承自object [].而int []和object []都继承自System.Array
编辑
我从Eric那里找到了更多关于为什么这种转换被允许用于引用类型的原因.
它被添加到CLR中,因为Java需要它,而CLR设计者希望能够支持类似Java的语言.然后我们将它添加到C#,因为它在CLR中.这个决定当时颇具争议,我对它并不是很满意,但现在我们无能为力.
但是请注意,C#的数组协方差规则实际上是从CLR的规则稍有不同,如所描述这里.简而言之,CLR的规则是"如果X与Y兼容,那么X []赋值与Y []"兼容.
编辑
另请参阅我的其他答案,我在发布此答案之前已将其删除,但此后已取消删除.