索引器通过引用获取还是 get+set?

Dar*_*tom 5 c# c#-7.0

这是我的课:

class MyArray<T>
{
    private T[] data;
    public MyArray(int size)
    {
        data = new T[size];
    }
    public MyArray(in T[] array)
    {
        data = new T[array.Length];
        for (int i = 0; i < array.Length; i++)
            data[i] = array[i];
    }
    public T this[int i] //I am talking about this indexer
    {
        get => data[i];
        set => data[i] = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

我应该像这样定义我的索引器,而不是像上面那样做吗?

public ref T this[int i]
{
    get => data[i];
}
Run Code Online (Sandbox Code Playgroud)

我应该选择哪种方法?据我所知,方法 1 允许您在设置值时执行额外的代码,但是方法 2 允许您执行类似的操作int.TryParse(str, out myArrayObj[i]);

编辑:这个问题不仅适用于索引器,而且适用于一般的任何属性。

And*_*nov 5

TL;DR如果您需要 setter,那么您必须使用常规索引器,因为ref属性不能有 setter。如果不需要设置器,那么我总是会在泛型ref类型参数的索引器/属性中指定关键字,因为它们可以是值类型。是否通过引用访问它们取决于消费者。

ref如果类型参数是引用类型(类),关键字不会提供任何好处,但对于值类型会有所不同,因为它们可以直接访问而无需复制。因此,如果您想访问数组中的原始值则必须使用ref关键字。如果您想访问数组中传递给类的值MyArray<T>,那么您不应该将这些值复制到其他私有数组,因为在这种情况下,您将获得私有数组中值的副本。

以下示例显示了 ref 访问与常规访问的不同之处:

[Fact]
public void ArrayTest()
{
    var structs = new[] {new MyStruct()};

    structs[0].Type++;

    ref var @struct = ref structs[0];
    @struct.Type++;

    // Test passes the value was incremented twice, because reference was used.
    structs[0].Type.Should().Be(2);
}

[Fact]
public void MyArrayTest()
{
    var structs = new MyArray<MyStruct>(new[] {new MyStruct()});

    // Compiler error, but was possible in earlier versions of C#
    // and would not modify the item in the array,
    // because value was copied before modification.
    // structs[0].Type++;

    var @struct = structs[0];
    @struct.Type++;

    // Value wasn't incremented, because it was copied on the stack.
    structs[0].Type.Should().Be(0);
}

[Fact]
public void MyRefArrayTest()
{
    var structs = new MyRefArray<MyStruct>(new[] {new MyStruct()});

    structs[0].Type++;

    ref var @struct = ref structs[0];
    @struct.Type++;

    // Test passes the value was incremented twice, because reference was used.
    structs[0].Type.Should().Be(2);
}

struct MyStruct
{
    public int Type { get; set; }
}

class MyArray<T>
{
    private T[] data;

    public MyArray(in T[] array)
    {
        data = new T[array.Length];
        Array.Copy(array, data, array.Length);
    }

    public T this[int i]
    {
        get => data[i];
        set => data[i] = value;
    }
}

class MyRefArray<T>
{
    private T[] data;

    public MyRefArray(in T[] array)
    {
        data = new T[array.Length];
        Array.Copy(array, data, array.Length);
    }

    public ref T this[int i]
    {
        get => ref data[i];

        // Compiler error, ref indexer cannot have setter.
        // set => data[i] = value;
    }
}
Run Code Online (Sandbox Code Playgroud)