数组索引器和其他对象索引器有什么区别?

本文关键字:索引 什么 区别 其他 数组 对象 | 更新日期: 2023-09-27 18:06:10

考虑以下两种数据类型:

class C
{
    public int I { get; set; }
}
struct S
{
    public int I { get; set; }
}

让我们尝试在列表中使用它们,例如:

var c_list = new List<C> { new C { I = 1 } };
c_list[0].I++;
var s_list = new List<S> { new S { I = 1 } };
s_list[0].I++; // (a) CS1612 compilation error

如预期的那样,(a): CS1612 Cannot modify the return value of 'List<UserQuery.S>.this[int]' because it is not a variable行出现编译错误。这很好,因为实际上我们试图修改S的临时副本,它在给定的上下文中是r值。

让我们试着对数组做同样的事情:

var c_arr = new[] { new C { I = 1 } };
c_arr[0].I++;
var s_arr = new[] { new S { I = 1 } };
s_arr[0].I++; // (b)

和. .这工作。

,

var s_arr_list = (IList<S>) s_arr;
s_arr_list[0].I++;

无法编译,

如果我们查看生成的IL,我们会发现以下内容:

IL_0057:  ldloc.1     // s_arr
IL_0058:  ldc.i4.0    // index
IL_0059:  ldelema     UserQuery.S // manager pointer of element

ldelema将数组元素的地址加载到求值堆栈的顶部。这样的行为是fixed数组和不安全指针所期望的。但为了安全起见,这有点出乎意料。为什么数组有一个特殊的不明显的情况?为什么不能为其他类型的成员实现相同的行为?

数组索引器和其他对象索引器有什么区别?

数组访问表达式被分类为变量。你可以给它赋值,通过引用传递等等。索引器访问是单独分类的…在分类列表中(c# 5规范第7.1节)

  • 索引器访问。每个索引器访问都有一个关联的类型,即索引器的元素类型。此外,索引器访问具有关联的实例表达式和关联的参数列表。当调用索引器访问的访问器(get或set块)时,计算实例表达式的结果成为this(§7.6.7)所表示的实例,并且计算实参列表的结果成为调用的形参列表。

可以认为这类似于字段和属性之间的区别:

 public class Test
 {
     public int PublicField;
     public int PublicProperty { get; set; }
 }
 ...
 public void MethodCall(ref int x) { ... }
 ...
 Test test = new Test();
 MethodCall(ref test.PublicField); // Fine
 MethodCall(ref test.PublicProperty); // Not fine

基本上,索引器是一对方法(或单个方法),而数组访问为您提供存储位置。

请注意,如果您一开始没有使用可变结构体,那么您不会看到这种方式的区别—我强烈建议不要使用可变结构体。

List<T>中那样的类索引器实际上是调用方法的一种语法上方便的方式。

对于数组,你实际上是在访问内存中的结构。在这种情况下没有方法调用