【问题标题】:Are C# readonly field's allowed to be modify outside of the class?是否允许在类之外修改 C# 只读字段?
【发布时间】:2018-05-28 19:57:27
【问题描述】:

我有一个通过构造函数参数设置的readonly 对象字段。如果我修改对象,类内的字段也会改变,我猜是引用调用。有什么方法可以更好地/防止它吗?

private void Form1_Load(object sender, EventArgs e)
{
    Product p = new Product() { Name="New" };
    Store s = new Store(p);
    p.Name = "MODIFY!";
    MessageBox.Show(s.Show());//MODIFY!
}

public class Store
{
    private readonly Product product;

    public Store(Product product)
    {
        this.product = product;
    }

    public string Show()
    {
        return this.product.Name;
    }
}

public class Product
{
    public string Name { get; set; }
}

【问题讨论】:

  • 声明变量为const
  • @SouvikGhosh 引用类型字段不能标记为const,除非分配了空引用,这与这里的观点相悖。
  • 不管是什么解决方案,除了 const 之外的任何东西都可以通过反射来改变。其他一切都只是编译器规则。
  • readonlyshallow 不变性(仅适用于它所应用的变量)。我认为您正在寻找的是 deep 不变性(递归地应用于它所应用的变量和引用类型的任何字段),这目前不能作为语言功能使用。跨度>

标签: c#


【解决方案1】:

您在readonly 字段中存储的是引用。当然,该引用是只读的,永远不会改变。但是引用对象的内容还是可以改变的。

由于Product 似乎是一个数据保存类,一种方法可能是将内容简单地复制到一个新实例中:

public class Store
{
    private readonly Product product;

    public Store(Product product)
    {
        // Create a new Product instance that only this Store instance
        // knows about
        this.product = new Product { Name = product.Name };
    }
}

现在Store.product的内容不能从外部改变,只要你不导出这个实例。
但请注意,Store内部的代码可能仍然能够更改内容。

【讨论】:

  • 虽然 OP 确实提到了 object,但可能的替代方法是将其声明为 struct。这意味着数据被复制进来,但假设数据结构很简单。
  • @DiskJunky 是的,使Product 成为struct 是一个很好的可能性。但我认为这将是对 OP 代码的更大更改(不知道 Product class 已在其他地方使用)。
  • @DiskJunky 将其设为struct 只是 以便轻松复制不是一个好主意,它会改变太多。如果 OP 想要一个对象的副本而不是对同一对象的引用,那么他需要制作一个副本。
  • @DaveCousineau 我完全同意。如果结构足够简单,我只是将其作为一种选择。目前尚不清楚这是新代码还是现有代码。
  • @DiskJunky 我怀疑你混淆了不变性(在这种情况下你想要的)和struct...struct 不能解决任何可变对象直接或间接引用的问题这种方式可以修改...即struct X { SomeMutableClass Y {get;} } 仍然有同样的问题,只是多了一个点。
【解决方案2】:

如果您不希望 Store 内部的 product 在更改原始实例时发生更改,则必须在将其分配给字段之前进行复制:

public Store(Product product)
{
    this.product = new Product() {Name = product.Name};
}

或者,您可以将Product 设为结构。结构体在传递给方法时总是被复制,并且通过引用传递:

public struct Product
{
    public string Name { get; set; }
}

【讨论】:

    【解决方案3】:

    另一种选择是创建 Product 的不可变版本:

    public class Product
    {
        public string Name { get; set; }
    
        public Immutable ToImmutable() => new Immutable(this);
    
        public class Immutable
        {
             public Immutable(Product product) { Name = product.Name; }
             public string Name { get; }
        }
    }
    

    现在,

    public class Store
    {
        private readonly Product.Immutable product;
    
        public Store(Product product)
        {
            this.product = product.ToImmutable();
        }
    
        public string Show()
        {
            return this.product.Name;
        }
    }
    

    好处?没有人可以在Store 里面乱搞product

    【讨论】:

      猜你喜欢
      • 2013-04-29
      • 2011-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 1970-01-01
      • 1970-01-01
      • 2017-12-22
      相关资源
      最近更新 更多