最近我遇到了很多需要开放和只读类的版本的场景。一个常见的场景是用户可以设置属性的设置类,但是当设置已经过验证并经历长时间运行的操作时,它们应该只能访问只读版本。
这些类不是通用商店,而且是强类型的。
目前我只是从读/写版本继承并在写入尝试时抛出异常,并且想知道是否有更简化的方式人们这样做。
答案 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");
}
}