2013-05-09 37 views
69

陣列在C#是共變隱式上引用類型爲什麼C#表現不同的兩個int數組語法

object[] listString = new string[] { "string1", "string2" }; 

但不是值類型,因此,如果你改變stringint,你會得到編譯錯誤:

object[] listInt = new int[] {0, 1}; // compile error 

現在,值得關注的是,當你聲明int陣列狀兩種語法下面不顯式聲明的類型int,僅僅只有區分上new[],編譯器會區別對待:

object[] list1 = { 0, 1 };  //compile successfully 
object[] list2 = new[] {0, 1}; //compile error 

你會得到object[] list1 = { 0, 1 };編譯成功,但object[] list2= new[] {0, 1};編譯錯誤。

看來C#編譯器將

object[] list1 = { 0, 1 }; 

object[] list1 = new object[]{ 0, 1 }; 

object[] list2 = new[] { 0, 1 }; 

object[] list2 = new int[]{ 0, 1 }; //error because of co-variant 

爲什麼C#編譯器在這種情況下的行爲方式不同?

+6

+1,你來自[這個問題](http://stackoverflow.com/q/16456507/961113) – Habib 2013-05-09 08:03:29

+0

@Habib:是的,從中得到啓發 – 2013-05-09 08:04:16

+0

快速猜測,所有的引用類型都採用相同的數字字節爲了便於參考,它易於隱含轉換。 – Jodrell 2013-05-09 08:21:12

回答

57

編譯版本使用數組初始化程序初始化list1。 C#語言規範,§1.110( 「數組初始」)規定:

An array initializer consists of a sequence of variable initializers, enclosed by 「{」and 「}」 tokens and separated by 「,」 tokens. Each variable initializer is an expression or, in the case of a multi-dimensional array, a nested array initializer.

The context in which an array initializer is used determines the type of the array being initialized. In an array creation expression, the array type immediately precedes the initializer, or is inferred from the expressions in the array initializer. In a field or variable declaration, the array type is the type of the field or variable being declared.

When an array initializer is used in a field or variable declaration, such as:

int[] a = {0, 2, 4, 6, 8}; 

it is simply shorthand for an equivalent array creation expression:

int[] a = new int[] {0, 2, 4, 6, 8}; 

所以很明顯,這應該編譯。

第二個版本使用明確的陣列創建表達式,其中您指示編譯器具體指定要創建的數組類型。 §1.51.10.4(「數組創建表達式」)規定:

An array creation expression of the third form is referred to as an implicitly typed array creation expression. It is similar to the second form, except that the element type of the array is not explicitly given, but determined as the best common type (§1.50.2.14) of the set of expressions in the array initializer.

因此,第二版本相當於

object[] list2 = new int[] { 0, 1 }; 

所以,現在的問題實際上就變成「爲什麼我不能分配給int[]一個object[]「,就像你在問題結尾處提到的一樣。而答案也很簡單,在§1.109給出(「陣列協」):

Array covariance specifically does not extend to arrays of value-types. For example, no conversion exists that permits an int[] to be treated as an object[] .

10

當您使用{}時,您使用集合初始值設定項(請參閱:http://msdn.microsoft.com/en-us/library/vstudio/bb384062.aspx)。這些括號之間的值必須放在某個地方。爲此,必須創建一個集合。編譯器將確定上下文以找出哪種集合。

如果第一個:object[] list1 = { 0, 1 };很明顯應該創建一個集合。但是它應該是什麼樣的?某處不存在new操作。只有一個提示:list1類型object[]。所以編譯器創建了這個集合,並用它來填充它。

在你的第二個例子object[] list1 = new[] { 0, 1 };還有另一個提示:new[]。這個提示明確地說:將會有一個數組。該數組沒有類型,因此它會嘗試通過分析值來查找數組的類型。這些都是int's,所以它會創建一個int的數組並填充它。其他提示object[]完全被忽略,因爲創建提示比應該分配的提示重要得多。現在編譯器想把這個數組分配給list1和BOOM:不適合!

1

數組初始化是編譯器方便。如果我說「我正在聲明一個對象數組並將其賦值」,那麼編譯器認爲您的對象數組是一個對象數組並將其解釋爲合理的。儘管語法似乎是一項任務,但並不是:您正在使用一個初始程序。這種語法的代表是object[] list1 = new object[] { 0, 1 }

當你說new[] { 0, 1 }時,這是一個表達式,它創建一個數組並初始化它。此表達式的計算與您分配的內容無關 - 而且由於編譯器檢測到隱式整數類型,它會創建int[]。該表達式的長形版本是object[] list2 = new int[] { 0, 1 }

如果比較這兩個陳述的長手版本,很明顯看到它們的不同之處。

27

聲明

object[] listInt = new int[] {0, 1}; 

因爲協變矩陣轉換是不允許的值類型(和int是值類型)是無效的。或者,聲明

object[] listInt = new string[] {"0", "1"}; 

是有效的,因爲參考類型允許協變數組轉換。這是因爲分配x = (object)myString只涉及簡單的分配,但y = (object)myInt需要進行裝箱操作。

現在談談兩個聲明之間的區別。在聲明object[] list2 = new[] { 0, 1 }中,由於類型推斷的工作原理,它首先查看右側的表達式並得出new[] { 0, 1 }應被視爲new int[] { 0, 1 }的結論。然後它嘗試將此int數組分配給對象數組,由於值類型問題的協變轉換而出現錯誤。但是,聲明object[] list1 = { 0, 1 }使用集合初始值設定項,在這些情況下,集合的類型是定義類型的位置,因此每個元素將轉換爲集合預期的類型。

2

聲明object[] list1 = { 0, 1 };編譯因爲編譯器是足夠聰明,知道你正在嘗試數字類型的數組轉換爲參考類型的數組,因此它將Int32元素放入引用類型中。

你也可以明確地框基本類型:

object[] list2 = Array.ConvertAll<int, Object>(new[] { 0, 1 }, input => (Object)input);

編譯器不會隱式做拳擊時,你已經「INT []」或指定「的Int32 []」作爲數組類型,但它似乎可以添加到C#中。

1
object[] listInt = new int[] {0, 1}; 

是簡寫

object[] listInt; 
listInt = new int[] {0, 1}; 

不因爲int[] is not covariant with object[]工作。

而當您說new[]時,它相當於new int[],因此同樣適用。

相關問題