[英]C#: Immutable class
我有一个应该在该类中不可变的类,我只有给索引器一个私有集合属性,所以为什么这不是不可变的,我可以在数组中设置一些字段,就像在主类中看到的那样...
class ImmutableMatice
{
public decimal[,] Array { get; private set; } // immutable Property
public ImmutableMatice(decimal[,] array)
{
Array = array;
}
public decimal this[int index1, int index2]
{
get { return Array[index1, index2]; }
}
........以及在main方法中,如果我用数据填充此类并更改数据
static void Main(string[] args)
{
decimal[,] testData = new[,] {{1m, 2m}, {3m, 4m}};
ImmutableMatice matrix = new ImmutableMatice(testData);
Console.WriteLine(matrix[0,0]); // writes 1
testData[0, 0] = 999;
Console.WriteLine(matrix[0,0]); // writes 999 but i thought it should
// write 1 because class should be immutable?
}
}
有什么方法可以使此类不变吗?
是的,解决方案是将数组复制到构造函数中的新数组,如下所示:
public ImmutableMatice(decimal[,] array)
{
decimal[,] _array = new decimal[array.GetLength(0),array.GetLength(1)];
//var _array = new decimal[,] { };
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
_array[i, j] = array[i, j];
}
}
Array = _array;
}
您的类是不可变的,但其中的对象却不是。
具有public decimal[,] Array { get; private set; }
public decimal[,] Array { get; private set; }
public decimal[,] Array { get; private set; }
将只保证你不能将属性设置Array
来的新实例Array
,但它不会阻止你访问现有的对象,并改变其值(并非一成不变)。
您可能需要查看适当命名的ReadOnlyCollection<T>
类。
正如@Mike所指出的,我是第一次来回看:这有点不对劲,因为您是通过testData
对象而不是通过matrix
访问值。 虽然原始观点仍然存在,但更确切地说,您遇到的问题是您正在更改传递了引用的基础对象中的值。 您将一起绕过ImmutableMatice
对象。
前面提到的使用ReadOnlyCollection<T>
解决方案仍然存在:通过在其周围创建此只读包装器,之后将无法再对其进行更改。 但是,只有当您按预期方式实际使用它时,才是这种情况:通过ImmutableMatice
而不是通过您仍然引用的基础集合。
解决此问题的另一种方法是将原始数组的内容复制到另一个数组,以将其与您仍然引用的数组“断开连接”。
为了说明这一点,请考虑以下示例。 第一个演示了如何仍然可以影响基础引用,而第二个演示了如何通过将值复制到新数组中来解决它。
void Main()
{
var arr = new[] { 5 };
var coll = new ReadOnlyCollection<int>(arr);
Console.WriteLine (coll[0]); // 5
arr[0] = 1;
Console.WriteLine (coll[0]); // 1
}
void Main()
{
var arr = new[] { 5 };
var arr2 = new int[] { 0 };
Array.Copy(arr, arr2, arr.Length);
var coll = new ReadOnlyCollection<int>(arr2);
Console.WriteLine (coll[0]); // 5
arr[0] = 1;
Console.WriteLine (coll[0]); // 5
}
那是因为您实际上是在更改数组中的数据,而不是在索引器中。
static void Main(string[] args)
{
decimal[,] testData = new[,] {{1m, 2m}, {3m, 4m}};
ImmutableMatice matrix = new ImmutableMatice(testData);
Console.WriteLine(matrix[0,0]); // writes 1
testData[0, 0] = 999; // <--- THATS YOUR PROBLEM
Console.WriteLine(matrix[0,0]); // writes 999 but i thought it should
// write 1 because class should be immutable?
}
您可以将数组复制到构造函数中的private属性中,以避免这种情况。
请注意,您确实不能编写matrix[0,0] = 999;
因为索引器没有设置器。
编辑
就像克里斯指出的那样(我自己怎么会错过它?)-您根本不应该将数组公开为属性(这意味着在大多数情况下甚至不必是属性)。
请考虑以下代码:
private decimal[,] _myArray; // That's private stuff - can't go wrong there.
public decimal this[int index1, int index2]
{
// If you only want to allow get data from the array, thats all you ever need
get { return Array[index1, index2]; }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.