繁体   English   中英

List之间有什么区别 <T> 和数组索引器?

[英]What is the difference between List<T> and array indexers?

在我的脑海中提出的问题是否可以从List中访问结构的引用以进行更改? reza线程。

因此,请考虑以下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]将引用返回到盒装结构,而在第二种情况下 - 它是另一种情况。

我也试图反汇编这个并发现:

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>如何通过索引返回元素? 或者这只是值类型的情况,与引用类型无关?

PS:我明白,一个不能改变的值类型实例,但所描述的问题,让我明白了,我从来没有意识到如何List<T>和数组的工作。

.Net可以使用ldelema指令(数组元素的加载地址) ldelema寻址数组元素。

这允许您直接在数组元素上操作而无需复制它们。 (这也是你可以将数组元素作为refout参数传递的原因)

List<T>没有这样的能力。 相反, list[i]只是list.get_Item(i)语法糖,这是一个返回结构副本的普通方法调用。

数组的索引器以类似于将其作为ref参数传递的方式使元素可用于以下代码。 任何其他类型的任何.net语言都不存在同样表现的机制。 允许索引访问的任何其他类型必须公开一对方法,其中一个方法将内部存储数据的副本提供给调用者的代码,其中一个方法将给出来自调用者代码的一些数据的副本,存储那些数据以某种方式。 这种限制在值类型中最明显,但在某些情况下也可能对引用类型有问题(例如,可以对T[]的元素执行Interlocked.ComapreExchange ,但不能对具有List<T>的元素执行)。

如果一个人正在设计一个自己的集合类型,可以通过提供一个ActOnItem成员来减轻对索引器的限制,从而允许像MyFancyList.ActOnItem(4, (ref Point it) => {it.X += 4;});这样的代码MyFancyList.ActOnItem(4, (ref Point it) => {it.X += 4;}); 提供一系列具有不同数量的附加ref参数的通用版本可能会有所帮助,这些参数将从调用者传递(例如, MyFancyList.ActOnItem(4, (ref MyThing it, ref Thing newValue, ref Thing compareValue) => Threading.Interlocked.CompareExchange(ref it, newValue, compareValue); )因为使用这样的方法可以避免使用lambdas来使用捕获的变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM