试图创建不可变的类-被忽略的私有集
本文关键字:创建 不可变 | 更新日期: 2023-09-27 18:25:58
我正在尝试创建一个不可变的类来表示矩阵。然而,当行和列中的私有setter正在工作时,我仍然可以修改元素的内容。如何正确创建一个不可变类?
var matrix = new Matrix(new double[,] {
{1,1,1,1},
{1,2,3,4},
{4,3,2,1},
{10,4,12, 6}});
matrix.Elements[1,1] = 30; // This still works!
矩阵类:
class Matrix
{
public uint Rows { get; private set; }
public uint Columns { get; private set; }
public double[,] Elements { get; private set; }
public Matrix(double[,] elements)
{
this.Elements = elements;
this.Columns = (uint)elements.GetLength(1);
this.Rows = (uint)elements.GetLength(0);
}
}
数组不是不可变的。你需要使用一个索引器来拥有一个不可变的类型:
class Matrix
{
public uint Rows { get; private set; }
public uint Columns { get; private set; }
private readonly double[,] elements;
public Matrix(double[,] elements)
{
// this will leave you open to mutations of the array from whoever passed it to you
this.elements = elements;
// this would be perfectly immutable, for the price of an additional block of memory:
// this.elements = (double[,])elements.Clone();
this.Columns = (uint)elements.GetLength(1);
this.Rows = (uint)elements.GetLength(0);
}
public double this[int x, int y]
{
get
{
return elements[x, y];
}
private set
{
elements[x, y] = value;
}
}
}
您可以通过在实例上使用索引器来使用它:
var matrix = new Matrix(new double[,] {
{1,1,1,1},
{1,2,3,4},
{4,3,2,1},
{10,4,12, 6}});
double d = matrix[1,1]; // This works, public getter
matrix[1,1] = d; // This does not compile, private setter
我不会公开数组,而是创建一个索引器:
public double this[int x, int y]
{
get
{
return elements[x,y];
}
}
然后,您可以像这样使用索引器:
matrix[1,1]
正如其他人所指出的,通过不公开内部数组,而是向外部世界公开索引器,您几乎可以实现不变性。
public class Matrix
{
private readonly double[,] _elements;
public uint Rows { get { return (uint)elements.GetLength(0);} }
public uint Columns { get { return (uint)elements.GetLength(1);} }
public Matrix(double[,] elements)
{
this._elements = elements;
}
public double this[int x, int y]
{
get
{
return elements[x, y];
}
}
}
现在您可以轻松访问您的元素:
matrix[1,2]
几乎使您的类不可变。为什么差一点?因为内部数组仍然是可变的,并且它是由调用者传递给您的,调用者稍后可能会修改它(错误或故意),从而破坏您的不变性。
如何防止这种缺陷?
最简单的方法是制作原始数组的副本(使用Clone()
方法或其他方法,由您选择),并存储该副本。这样,您的内部数组就永远不会暴露于类之外的任何东西,从而实现真正的外部不变性(当然,除了任何反射或不安全的代码)。
实现不变性的另一个不错的选择是使用命名空间System.Collections.Immutable中的元素作为构建块。拥有已经不可变的集合有助于实现更复杂的场景。
这是另一种方法。。。
class Matrix
{
private uint Rows { get; private set; }
private uint Columns { get; private set; }
private double[,] Elements { get; private set; }
public Matrix(double[,] elements)
{
this.Elements = elements;
this.Columns = (uint)elements.GetLength(1);
this.Rows = (uint)elements.GetLength(0);
}
public double GetElement(int row, int column)
{
return this.elements[row, column];
}
}
将数组设为私有数组,并公开一个用于检索值的方法。