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

问题描述

为什么即使没有实现 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;
}

解决方法

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

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

例如,这不会编译:

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 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
    }
}

使用下面的整数数组包装类:

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();
}
,

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

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

    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] 这样的多维数组将按列优先顺序存储它,而大多数类似图像的数据通常按行优先顺序存储。