如何制作类的只读版本?

时间:2010-04-27 20:14:53

标签: c# .net readonly-attribute

我有一个包含各种公共属性的类,我允许用户通过属性网格进行编辑。对于持久性,此类也通过DataContractSerializer与XML文件进行序列化/反序列化。

有时我希望用户能够将他们所做的更改保存(序列化)到类的实例中。但在其他时候,我不想让用户保存他们的更改,而应该将属性网格中的所有属性都看作只读。我不想让用户进行以后永远无法保存的更改。类似于MS Word将允许用户打开当前由其他人打开但仅作为只读的文档。

我的类有一个布尔属性,用于确定该类是否应该是只读的,但是是否可以使用此属性以某种方式在运行时动态地向类属性添加只读属性?如果不是什么是替代解决方案?我应该将我的类包装在只读包装类中吗?

6 个答案:

答案 0 :(得分:19)

不变性是一个C#仍有改进空间的领域。虽然可以创建具有readonly属性的简单不可变类型,但是一旦你需要更复杂的控制何时类型是可变的,你就开始了遇到障碍。

您有三种选择,具体取决于您需要“强制执行”只读行为的强度:

  1. 在您的类型中使用只读标志(就像您正在做的那样)并让调用者负责不尝试更改该类型的属性 - 如果进行了写入尝试,则抛出一个异常。

  2. 创建一个只读接口,让您的类型实现它。这样,您可以通过该接口将类型传递给只应执行读取的代码。

  3. 创建一个聚合您的类型的包装器类,只显示读取操作。

  4. 第一个选项通常是最简单的,因为它可以减少对现有代码的重构,但是为类型的作者提供了在实例不可变时与不实例时通知消费者的最小机会。此选项还提供编译器在检测不当使用时的最少支持 - 并将错误检测降级到运行时。

    第二个选项很方便,因为在没有太多重构努力的情况下实现接口是可能的。不幸的是,调用者仍然可以转换为基础类型并尝试对其进行编写。通常,此选项与只读标志组合以确保不会违反不变性。

    就强制执行而言,第三种选择是最强的,但它可能导致代码重复,更多的是重构工作。通常,组合选项2和3非常有用,可以建立只读包装器和可变类型多态的关系。

    就个人而言,我倾向于在编写新代码时使用第三个选项,我希望在那里强制执行不变性。我喜欢不可能“抛弃”不可变包装器这一事实,并且它通常允许您避免将杂乱的if-read-only-throw-exception检查写入每个setter。

答案 1 :(得分:2)

为什么不能这样:

private int someValue;
public int SomeValue
{
    get
    {
         return someValue;
    }
    set
    {
         if(ReadOnly)
              throw new InvalidOperationException("Object is readonly");
         someValue= value;
    }

答案 2 :(得分:1)

我会使用一个包装类来保持所有内容都是只读的。这是为了可扩展性,可靠性和一般可读性。

我没有预见到任何其他方法可以提供上述三个好处以及 more 。在我看来,绝对使用包装类。

答案 3 :(得分:0)

这样的事情会有所帮助:

class Class1
{
    private bool _isReadOnly;

    private int _property1;
    public int Property1
    {
        get
        {
            return _property1;
        }
        set
        {
            if (_isReadOnly) 
              throw new Exception("At the moment this is ready only property.");
            _property1 = value;
        }
    }
}

设置属性时需要捕获异常。

我希望这是你正在寻找的东西。

答案 4 :(得分:0)

通过在运行时将属性更改为只读,您无法获得编译时检查(如关键字readonly所示)。所以没有别的方法可以手动检查并抛出异常。

但可取的是,重新设计对课程的访问权限更好。例如,创建一个“编写器类”,它检查当前是否可以编写基础“数据类”。

答案 5 :(得分:0)

当_readOnly设置为true时,您可以使用PostSharp创建不会将新值传递给任何字段的OnFieldAccessAspect。随着方面代码重复的消失,将不会忘记任何字段。