维护同一类的开放和只读版本

时间:2015-02-13 17:45:45

标签: c# .net readonly

最近我遇到了很多需要开放和只读类的版本的场景。一个常见的场景是用户可以设置属性的设置类,但是当设置已经过验证并经历长时间运行的操作时,它们应该只能访问只读版本。

这些类不是通用商店,而且是强类型的。

目前我只是从读/写版本继承并在写入尝试时抛出异常,并且想知道是否有更简化的方式人们这样做。

5 个答案:

答案 0 :(得分:1)

为什么不使用界面?将对象作为您希望它只读取的接口传递,但将其作为具体类型传递给您希望它可读写的地方。

    public interface IUserSettings
    {
        int Value1 { get; }
        string Value2 { get; }
    }

    public class UserSettings : IUserSettings
    {
        public int Value1 { get; set; }
        public string Value2 { get; set; }
    }

然后,您还可以更新UI以不同方式显示UserSettings和IUserSettings(即,1个模板显示编辑控件,1个模板显示只读控件。)

答案 1 :(得分:1)

首先,请注意"只读"和"不可变的"。让我们假设您为r("接收者")提供对象o(" object")的引用:

  • 如果您只是想确保r无法更改o的值,那么基于接口的解决方案(如this one)就足够了可能就像它会变得容易一样。

    var o = new List<int> { 1, 2, 3 };
    r.LookAtList((IEnumerable<int>)o);
    

    r.LookAtList会将o视为只读序列,因为IEnumerable<>抽象是只读的。

  • 如果您还想确保r始终遵守o的相同值,那么基于接口的解决方案就不够了。考虑一下:

    var o = new List<int> { 1, 2, 3 };
    r.LookAtList((IEnumerable<int>)o);
    o.Add(4);
    r.LookAtList((IEnumerable<int>)o);
    

    虽然r.LookAtList无法更改原始对象o(除非它使用反射或将IEnumerable<>强制转换为List<>),但它将第二次观察到不同的序列,即使它通过了完全相同的IEnumerable<>引用。

如果您真的想要某种类型的读写版本和不可变版本,那么以两种不同的类型结束。我建议您考虑Builder pattern

sealed class FooBuilder
{
    public TA A { get; set; }
    public TB B { get; set; }
    …
    public Foo Build() { return new Foo(A, B, …); }
}

sealed class Foo
{
    public Foo(TA a, TB b, …)
    {
        … // validate arguments
        this.a = a;
        this.b = b;
        …
    }

    private readonly TA a;
    private readonly TB b;
    …

    public TA A { get { return a; } }
    public TB B { get { return b; } }
    …
}

但这非常冗长,你可能希望绕过所有重复。不幸的是,实现真正不可变类型需要在当前版本的C#编程语言中进行大量冗长。

在即将推出的C#版本(6)中,这可能会有所改变。

答案 2 :(得分:0)

我的第一个想法是使用具有默认值属性的结构FooDefaults,并将其传递给Foo(您的属性类)的构造函数,该构造函数使用默认值(在验证之后)初始化从只读属性返回的成员。

答案 3 :(得分:0)

Microsoft在WPF中使用Freezable层次结构所使用的模式正是您所描述的。例如,请参阅Freezable.WritePreamble

        if (IsFrozenInternal)
        {
            throw new InvalidOperationException(
                SR.Get(SRID.Freezable_CantBeFrozen,GetType().FullName));
        }

Freezable使用&#39; IsFrozen`让客户判断一个对象是否是不可变的。

答案 4 :(得分:0)

为什么不关闭套装呢?

private string prop1 = string.Empty;
public string Prop1 
{
    get { return prop1; }
    set 
    {
        if (ValueIsValid(prop1))
        {
            NotifyPropertyChanged("Prop1");
            return;   // or you can throw an exeption 
        }
        if (prop1 == value)  return;
        prop1 = value;
        NotifyPropertyChanged("Prop1");
    }
}