如何将引用类型属性设置为“只读”
-
22-08-2019 - |
题
我有课 Bar
具有包含引用类型的私有字段 Foo
. 。我想曝光 Foo
在公共财产中,但我不希望该财产的消费者能够改变 Foo
...然而,它应该可以通过内部更改 Bar
, , IE。我无法进入战场 readonly
.
所以我想要的是:
private _Foo;
public Foo
{
get { return readonly _Foo; }
}
……这当然是无效的。我可以返回一个克隆 Foo
(假设是 IClonable
),但这对消费者来说并不明显。我应该将房产名称更改为 FooCopy
??它应该是一个 GetCopyOfFoo
方法代替?您认为最佳实践是什么?谢谢!
解决方案
听起来您正在寻找 C++ 中的“const”等价物。这在 C# 中不存在。没有办法表明消费者不能修改对象的属性,但其他东西可以(当然假设变异成员是公共的)。
您可以按照建议返回 Foo 的克隆,或者可能返回 看法 到 Foo 上,如 只读集合 适合收藏。当然如果你能做到 Foo
不可变的类型,这会让生活变得更简单......
请注意,制作之间存在很大差异 场地 只读并使对象本身不可变。
目前,类型本身可以从两个方面改变事情。它可以做:
_Foo = new Foo(...);
或者
_Foo.SomeProperty = newValue;
如果它只需要能够执行第二个操作,则该字段可以是只读的,但仍然存在获取属性的人能够改变对象的问题。如果只需要做第一个,并且实际上 Foo
要么已经不可变,要么可以变得不可变,您只需提供一个仅具有“getter”的属性就可以了。
它是 很重要 您了解更改字段的值(使其引用不同的实例)和更改字段引用的对象的内容之间的区别。
其他提示
不幸的是,有解决这个没有简单的方法在C#中的时刻。您可以在一个界面中提取Foo
的“只读部分”,让你的财产回报,而不是Foo
。
进行复印,一ReadOnlyCollection
,或有Foo
是不可改变通常是三个最佳途径,因为你已经猜测。
我有时青睐方法而不是每当我做任何事情不是简单地返回底层的领域,这取决于有多少工作涉及多个显著性。方法暗示的东西是怎么回事,当他们使用你的API筹集更多的消费者一个标志。
“克隆”的对象富收到,并给回了被称为正常的做法守复制。除非有一些看不见的副作用克隆,这将是对用户可见,也绝对没有理由不这样做。它往往是为了保护你的类内部的私有数据,尤其是在C#或Java,其中const
的C ++的想法是不可用的唯一途径。 (即,它必须以正确地创建在这两种语言真正不可改变的对象进行。)
喜欢你的用户只是为了澄清,可能的副作用是事情(合理的)期待的是,原来的对象被退回,或富,将无法正确克隆正在举行的一些资源。 (在这种情况下,它在干什么实施IClonable?!)
如果你不希望任何人来惹你的国家......不要将其暴露!正如其他人所说,如果有什么需要查看内部状态,提供它的一个不可改变的表示。或者,让客户告诉的您的做一些事情(谷歌“告诉不问”),而不是做它自己。
要澄清乔恩斯基特的评论,你可以做一个观点,那就是对可变富不可变的包装类。下面是一个例子:
class Foo{
public string A{get; set;}
public string B{get; set;}
//...
}
class ReadOnlyFoo{
Foo foo;
public string A { get { return foo.A; }}
public string B { get { return foo.B; }}
}
您可以真正重现C ++常量的行为在C#中 - 你不得不做手工
。无论Foo
是,主叫方可以修改它的状态的唯一方式是通过调用它的方法或设置属性。
例如,Foo
是类型FooClass
的:
class FooClass
{
public void MutateMyStateYouBadBoy() { ... }
public string Message
{
get { ... }
set { ... }
}
}
所以你的情况,你是为他们高兴得到Message
财产,但不设置它,你肯定对他们调用该方法不开心。
所以,定义一个接口描述他们允许做什么:
interface IFooConst
{
public string Message
{
get { ... }
}
}
我们已经离开了,突变方法,只留下对属性的getter。
然后该接口添加到FooClass
的基列表。
现在与Foo
物业类,你有一个字段:
private FooClass _foo;
和一个属性getter:
public IFooConst Foo
{
get { return _foo; }
}
这基本上是通过手再现恰恰是C ++ const
关键字将自动执行。在伪C ++术语,类型const Foo &
的参考就像是一个自动生成的类型仅包括被标记为Foo
成员const
的那些成员。这个翻译成C#的一些理论以后的版本中,你会声明FooClass
这样的:
class FooClass
{
public void MutateMyStateYouBadBoy() { ... }
public string Message
{
get const { ... }
set { ... }
}
}
真的所有我所做的就是合并了IFooConst
信息回FooClass
,通过用新const
关键字标记的一个安全员。因此,在某种程度上,增加一个const关键字不会对语言增加多少,除了正式的办法处理这一模式。
然后,如果你有const
参照FooClass
对象:
const FooClass f = GetMeAFooClass();
您将只能够使用const成员呼吁f
。
请注意,如果FooClass
定义是公共的,呼叫者可施放IFooConst
成FooClass
。但他们能做到这一点在C ++太 - 这就是所谓的“虚掷const
”,涉及一个名为const_cast<T>(const T &)
特殊的运算符
还有不是很容易你的产品的版本之间的进化接口的问题。如果第三方可以实现你定义一个接口(他们可以自由地做,如果他们能看到它),那么你就不能无需他人重新编译他们的代码添加在将来的版本新的方法吧。但是,这只是一个问题,如果你正在编写一个可扩展库他人的基础上。也许内置const
功能将解决这个问题。
我在考虑类似的安全的东西。有可能是一个办法。十分清楚,但是也不短。总的想法是相当简单的。不过我总是发现一些方法使周围从来没有测试它。但是,你可以检查它 - 也许它会为你工作。
这是伪代码,但我希望它背后的思路是清晰的。
public delegate void OnlyRuller(string s1, string s2);
public delegate void RullerCoronation(OnlyRuller d);
class Foo {
private Foo();
public Foo(RullerCoronation followMyOrders) {
followMyOrders(SetMe);
}
private SetMe(string whatToSet, string whitWhatValue) {
//lot of unclear but private code
}
}
所以它创建这个属性,你可以访问SetMe方法,但它仍然是私人因此除了创作者美孚看起来unmutable类。
不过比一些属性,还有更大的,这将可能成为超级很快混乱 - 这就是为什么我总是选择封装的其他方式。但是,如果你不允许客户端更改富比,这是一个替代它的超级重要。
然而,正如我说,这是唯一的理论