2012-11-21 44 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);)参数。

相关问题