2012-11-21 70 views
6

Is it possible to access a reference of a struct from a List to make changes?reza在我腦海中提出的問題。列表<T>和數組索引器之間有什麼區別?

所以,請考慮以下structinterface(definetely不是非常有用,但只是爲了顯示這個問題):

public interface IChangeStruct 
{ 
    int Value { get; } 
    void Change(int value); 
} 

public struct MyStruct : IChangeStruct 
{ 
    int value; 

    public MyStruct(int _value) 
    { 
     value = _value; 
    } 

    public int Value 
    { 
     get 
     { 
      return value; 
     } 
    } 

    public void Change(int value) 
    { 
     this.value = value; 
    } 
} 

MyStruct實現IChangeStruct,所以我們可以直接在堆改變它的盒裝拷貝無需拆箱並換上新的。這可以用下面的代碼實例闡述:

MyStruct[] l1 = new MyStruct[] 
{ 
    new MyStruct(0) 
}; 

Console.WriteLine(l1[0].Value); //0 
l1[0].Change(10); 
Console.WriteLine(l1[0].Value); //10 

現在,讓我們改變陣列List<T>,即:

List<MyStruct> l2 = new List<MyStruct> 
{ 
    new MyStruct(0) 
}; 

Console.WriteLine(l2[0].Value); //0 
l2[0].Change(10); 
Console.WriteLine(l2[0].Value); //also 0 

據我瞭解,在第一種情況下l1[0]返回referense到盒裝結構,而在第二個 - 它是別人。

我也試圖拆開,發現這個:

1)MyStruct[]

IL_0030: ldelema Utils.MyStruct 
IL_0035: ldc.i4.s 10 
IL_0037: call  instance void Utils.MyStruct::Change(int32) 

2)List<MyStruct>

IL_007c: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<valuetype Utils.MyStruct>::get_Item(int32) 
IL_0081: stloc.s CS$0$0001 
IL_0083: ldloca.s CS$0$0001 
IL_0085: ldc.i4.s 10 
IL_0087: call  instance void Utils.MyStruct::Change(int32) 

但我似乎是沒有準備好解釋它。

那麼,List<T>返回什麼?或者如何做array和List<T>按索引返回元素?或者這只是值類型的情況,並且與引用類型無關?

P.S:我明白,一個不得變化的值類型的實例,但所描述的問題,讓我明白了,我從來沒有意識到如何List<T>和陣列的工作。

回答

9

.Net可以使用ldelema指令(數組元素的加載地址)就地尋址數組元素。

這允許您直接操作數組元素而不復制它們。 (這也是爲什麼你可以通過一個數組元素作爲refout參數)

List<T>沒有這樣的能力。相反,list[i]只是list.get_Item(i)的語法糖,它是返回結構副本的常規方法調用。

+1

數組只有0的索引和單維纔有這種能力嗎? (因爲'ldelema'只能在這些限制下工作) – Earlz

3

數組的索引器以類似於將其作爲ref參數傳遞的方式使元素可用於以下代碼。任何.net語言都不存在任何其他類型的機制來表現同樣的行爲。允許索引訪問的任何其他類型必須公開一對方法,其中一個方法使內部存儲的數據的副本可用於調用者的代碼,其中一個方法將從調用者的代碼獲取一些數據的副本,該數據以某種方式。這種限制對於數值類型來說是最明顯的,但在某些情況下也可能對參考類型有問題(例如,可能對T[]中的元素執行Interlocked.ComapreExchange,但對List<T>的元素不執行此操作)。

如果您正在設計自己的收藏類型,可以通過提供ActOnItem成員來緩解索引者的限制,從而允許使用MyFancyList.ActOnItem(4, (ref Point it) => {it.X += 4;});之類的代碼。提供一系列具有不同號碼的附加ref參數的通用版本可能會有幫助,因爲使用這種方法可以避免需要使用捕獲的變量的lambda表達式,所以可以從調用者傳遞(例如MyFancyList.ActOnItem(4, (ref MyThing it, ref Thing newValue, ref Thing compareValue) => Threading.Interlocked.CompareExchange(ref it, newValue, compareValue);)參數。

相關問題