如何在不使类型成为结构体的情况下防止对集合中的对象进行修改

本文关键字:集合 对象 修改 情况下 类型 结构体 | 更新日期: 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;
      }
    }
  }