C# “如何创建引用类型属性”;只读“;
我有一个类C# “如何创建引用类型属性”;只读“;,c#,properties,encapsulation,readonly,C#,Properties,Encapsulation,Readonly,我有一个类Bar,它有一个包含引用类型Foo的私有字段。我想在公共财产中公开Foo,但我不希望财产的消费者能够改变Foo。。。但是,它应该可以通过Bar在内部进行更改,即,我不能将字段设置为只读 所以我想说的是: private _Foo; public Foo { get { return readonly _Foo; } } …这当然是无效的。我可以返回Foo的克隆(假设它是IClonable),但这对消费者来说并
Bar
,它有一个包含引用类型Foo
的私有字段。我想在公共财产中公开Foo
,但我不希望财产的消费者能够改变Foo
。。。但是,它应该可以通过Bar
在内部进行更改,即,我不能将字段设置为只读
所以我想说的是:
private _Foo;
public Foo
{
get { return readonly _Foo; }
}
…这当然是无效的。我可以返回
Foo
的克隆(假设它是IClonable
),但这对消费者来说并不明显。我是否应该将属性的名称更改为foopcopy
??它应该是GetCopyOfFoo
方法吗?你认为最好的练习是什么?谢谢听起来好像你是在从C++中得到“const”的等价物。这在C#中不存在。没有办法表明使用者不能修改对象的属性,但是其他东西可以(当然,假设变异的成员是公共的)
您可以按照建议返回Foo的克隆,或者可能返回Foo上的视图,就像对集合一样。当然,如果你能使Foo
成为一个不可变的类型,那会使生活更简单
请注意,将字段设置为只读与将对象本身设置为不可变之间有很大区别
目前,类型本身可以在两个方面进行更改。它可以做到:
_Foo = new Foo(...);
或
如果它只需要能够执行第二个操作,那么该字段可以是只读的,但仍然存在这样的问题:获取属性的人能够改变对象。如果它只需要执行第一步,并且实际上Foo
已经是不可变的或者可以被设置为不可变的,那么您只需提供一个只有“getter”的属性就可以了
了解更改字段的值(使其引用不同的实例)与更改字段引用的对象的内容之间的区别非常重要。复制、
ReadOnlyCollection
或使Foo
不可变通常是三条最佳途径,正如你已经推测的那样
当我做任何比简单返回基础字段更重要的事情时,我有时倾向于使用方法而不是属性,这取决于所涉及的工作量。方法意味着某些事情正在发生,当消费者使用您的API时,会给他们带来更大的影响。克隆您接收和返回的Foo对象是一种称为“防御性复制”的正常做法。除非克隆有一些用户可以看到的看不见的副作用,否则绝对没有理由不这样做。它通常是保护类内部私有数据的唯一方法,尤其是在C++或java中,C++代码的“代码> const < /C>”是不可用的。(也就是说,必须这样做才能在这两种语言中正确地创建真正不可变的对象。)
只是想澄清一下,可能的副作用是您的用户(合理地)期望返回原始对象,或者Foo持有的某些资源无法正确克隆。(在这种情况下,它在实现IClonable做什么?!)不幸的是,目前在C#中没有简单的解决方法。您可以在接口中提取
Foo
的“只读部分”,并让属性返回该部分,而不是Foo
为了澄清Jon Skeet的评论,您可以创建一个视图,它是可变Foo的一个不可变包装器类。下面是一个例子:
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++ const的行为,你只需要手动完成。 无论
Foo
是什么,调用方修改其状态的唯一方法是调用其上的方法或设置属性
例如,Foo
的类型为FooClass
:
class FooClass
{
public void MutateMyStateYouBadBoy() { ... }
public string Message
{
get { ... }
set { ... }
}
}
因此,在您的情况下,您很高兴他们获得Message
属性,但不设置它,而且您肯定不高兴他们调用该方法
因此,定义一个接口来描述他们可以做什么:
interface IFooConst
{
public string Message
{
get { ... }
}
}
我们省略了mutating方法,只剩下属性的getter
然后将该接口添加到FooClass
的基本列表中
现在,在使用Foo
属性的类中,您有一个字段:
private FooClass _foo;
和属性获取程序:
public IFooConst Foo
{
get { return _foo; }
}
<>这基本上是手工复制的,C++的代码> const < /c>关键字会自动完成。在psuedo-C++术语中,类型为const-Foo&
的引用类似于自动生成的类型,只包括标记为const
成员的Foo
的成员。将此转换为C#的理论未来版本,您可以这样声明FooClass
:
class FooClass
{
public void MutateMyStateYouBadBoy() { ... }
public string Message
{
get const { ... }
set { ... }
}
}
实际上,我所做的就是将IFooConst
中的信息合并回FooClass
,用一个新的const
关键字标记一个安全成员。因此,在某种程度上,添加一个const关键字不会给语言带来太多的变化,除了对这种模式的正式方法之外
然后,如果对FooClass
对象有const
引用:
const FooClass f = GetMeAFooClass();
您只能在f
上调用const成员
请注意,如果FooClass
定义是公共的,则调用方可以将IFooConst
强制转换为FooClass
。但是他们也可以在C++中这样做,它被称为“铸造<代码> const < /C>”,并涉及一个特殊的操作符,叫做“代码> conista C铸造(const t &))/c>
还有一个问题是
const FooClass f = GetMeAFooClass();
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
}
}