如何在不使类型成为结构体的情况下防止对集合中的对象进行修改
本文关键字:集合 对象 修改 情况下 类型 结构体 | 更新日期: 2023-09-27 17:50:12
使用以下示例代码:
class Program
{
static void Main( string[] args )
{
Stack<Data> s = new Stack<Data>();
Data d = new Data( 10 );
s.Push( d );
d.Value = 20;
Console.ReadKey();
}
}
class Data
{
public int Value;
public Data( int value )
{
Value = value;
}
}
如果不使Data
成为struct
,我是否有办法阻止d
实例被放在堆栈上,使其对象属性由d.Value = 20
行更新?当d
被压入堆栈时,它的Value
属性被赋值为10
,但当该属性被修改时,它被更改为20
。有没有其他方法可以阻止这种情况而不诉诸结构体或克隆类?
我问这个问题是因为我正在编写一个国际象棋引擎,作为搜索和评估的一部分,它需要在棋盘上移动棋子。当把板放在堆栈上时,这样我就可以创建子分析板,它会影响堆栈上已经存在的板。
没有办法将Board
类转换为struct
,因为它太大了,并且有很多引用类型,所以我决定不使用Stack对象来存储棋盘的实例。
相反,我创建了一个BoardManager
静态类来将所有单独创建的棋盘存储在一个列表中,当我需要某个棋盘时,我只需使用棋盘的标识符检索它。
你可以让你的Data类实现一个只读接口:
public interface IReadOnlyData {
int SomeInt { get;}
string SomeString {get;}
}
那么你的Stack可以是:
Stack<IReadOnlyData> stack = new Stack<IReadOnlyData>();
不行。由于使用了引用类型,因此应该更改所有实例。如果你使用简单类型值如你之前提到的,我建议你使用对象。MemberwiseClone方法。
更新:
这个实现如何:
static void Main(string[] args)
{
Stack<Data> s = new Stack<Data>();
Data d = new Data(10);
d.Freeze();
s.Push(d);
d.Value = 20;
Console.WriteLine("Frozen value : {0}", s.Pop().Value);
Console.WriteLine("actual value : {0}", d.Actual(data => data.Value));
Console.ReadKey();
}
class FreezableValue<T>
{
private T frozenValue;
private T actualValue;
private bool isFrozen;
public void Freeze()
{
isFrozen = true;
}
public T ActualValue
{
get
{
return actualValue;
}
set
{
this.actualValue = value;
if(!isFrozen)
frozenValue = value;
}
}
public T FrozenValue { get { return frozenValue; } }
}
class Data
{
private readonly FreezableValue<int> freezableValue;
private bool useActualValue;
public Data(int value)
{
freezableValue = new FreezableValue<int> { ActualValue = value };
useActualValue = true;
}
public void Freeze()
{
freezableValue.Freeze();
useActualValue = false;
}
public int Value
{
get { return useActualValue ? freezableValue.ActualValue : freezableValue.FrozenValue; }
set { freezableValue.ActualValue = value; }
}
public T Actual<T>(Func<Data, T> func)
{
useActualValue = true;
try
{
return func(this);
}
finally
{
useActualValue = false;
}
}
}