数组索引器和任何其他对象索引器之间有什么区别

lor*_*ond 9 c# arrays indexer data-structures

考虑以下两种数据类型:

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

struct S
{
    public int I { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,在线编译错误(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)
Run Code Online (Sandbox Code Playgroud)

而且..这很有效.

var s_arr_list = (IList<S>) s_arr;
s_arr_list[0].I++;
Run Code Online (Sandbox Code Playgroud)

将无法按预期编译.

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

IL_0057:  ldloc.1     // s_arr
IL_0058:  ldc.i4.0    // index
IL_0059:  ldelema     UserQuery.S // manager pointer of element
Run Code Online (Sandbox Code Playgroud)

ldelema将数组元素的地址加载到评估堆栈的顶部.fixed数组和不安全指针会出现这种行为.但是对于安全的环境,这有点出乎意料.为什么阵列有一个特殊的不明显的例子?为什么没有选择为其他类型的成员实现相同的行为?

Jon*_*eet 10

数组访问表达式被分类为变量.您可以分配给它,通过引用等方式传递它.索引器访问在分类列表中单独分类...(C#5规范第7.1节).

  • 索引器访问.每个索引器访问都有一个关联的类型,即索引器的元素类型.此外,索引器访问具有关联的实例表达式和关联的参数列表.当调用索引器访问的访问器(get或set块)时,评估实例表达式的结果将成为由此表示的实例(第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
Run Code Online (Sandbox Code Playgroud)

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

请注意,如果你没有使用可变结构开头,你就不会看到这种方式的区别 - 我强烈建议不要使用可变结构.