为什么即使 setter 未实现也可以设置二维数组

Why is it possible to set a 2d array even if setter not implemented

为什么即使 setter 没有实现也可以设置二维数组?

这是我的代码。

public static void Main()
{
    var obj = new TestObj();
    obj.qwe[0, 0] = 6;
    obj.asd = 7; //error
    Console.WriteLine(obj.qwe[0, 0]); //6
}

public class TestObj
{
    public int[,] qwe { get; } = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
    public int asd { get; } = 4;
}

Why is it possible to set a 2d array even if the setter not implemented?

因为您没有设置 属性,所以您正在更新数组中的项目。

例如,这不会编译:

obj.qwe = new int[0, 0];

在您的代码中,您正在调用 get 以获取对数组的引用,并更新索引 0、0 处的项目:

obj.qwe[0, 0] = 6;

属性 qwe 正在返回对数组内容的引用。这同样适用于一维或多维阵列。 C# 中的所有数组都遵循引用语义

考虑以下因素

int[,] temp = obj.qwe;  // property get
temp[0,0] = 6;
// obj.qwe[0,0] == 6

为防止这种情况发生,您需要为二维数组创建一个包装器 class 并实现一个索引器。

class Program
{
    static void Main(string[] args)
    {
        IntArray obj = new int[,] { { 1, 2 }, { 3, 4 } };
        int x = obj[0, 0];  // ok, getter defined
        obj[0, 0] = 10;     // error, no setter defined
        int[,] objCopy = obj;  // assignment to int[,] makes a copy
        objCopy[0, 0] = 10; // ok to modify copy
    }
}

使用下面的整数数组包装器 class:

public class IntArray
{
    readonly int[,] data;

    public IntArray(int rows, int columns)
    {
        this.data = new int[rows, columns];
        this.Rows = rows;
        this.Columns = columns;
    }
    public IntArray(int[,] data)
    {
        this.data = data;
        this.Rows = data.GetLength(0);
        this.Columns = data.GetLength(1);
    }
    public int this[int row, int column]
    {
        // indexer getter
        get => data[row, column];
    }
    public int Rows { get; }
    public int Columns { get; }
    public int[,] ToArray()
    {
        int[,] copy = new int[Rows, Columns];
        Array.Copy(data, copy, data.Length);
        return copy;
    }
    public static implicit operator IntArray(int[,] array)
        => new IntArray(array);
    public static implicit operator int[,](IntArray obj)
        => obj.ToArray();
}

为什么会这样,其他人已经回答过了

要使 属性 只读,我建议为二维数组创建您自己的 class。这允许您实现一个只读接口,而且相当容易做到:

    public interface IReadOnlyArray2D<out T>
    {
        T this[int x, int y] { get; }
    }
    public class MyArray2D<T> : IReadOnlyArray2D<T>
    {
        public int Width { get; }
        public int Height { get; }
        public T[] Data { get; }

        public MyArray2D(int width, int height )
        {
            this.Width = width;
            this.Height = height;
            Data = new T[width * height];
        }
        public T this[int x, int y]
        {
            get => Data[y * Width + x];
            set => Data[y * Width + x] = value;
        }
    }

使用常规一维数组作为后备存储还有其他优势:

  • 有时你只想处理所有项目,而不关心x/y坐标。
  • 如果使用不当,多维数组有时会很慢。
  • 互操作性通常更容易,因为更多系统在交换数据时使用一维数组或指针。
  • 创建像 new T[width, height] 这样的多维数组将按列优先顺序存储它,而大多数类似图像的数据通常按行优先顺序存储。