C# 通过公共只读属性写入对私有对象的访问
我现在正在努力理解我刚才在某处看到的东西 假设我有两门课:C# 通过公共只读属性写入对私有对象的访问,c#,properties,encapsulation,readonly,C#,Properties,Encapsulation,Readonly,我现在正在努力理解我刚才在某处看到的东西 假设我有两门课: class MyFirstCLass{ public int membVar1; private int membVar2; public string membVar3; private string membVar4; public MyFirstClass(){ } } 以及: 如果我这样做: var secondClassObject = new
class MyFirstCLass{
public int membVar1;
private int membVar2;
public string membVar3;
private string membVar4;
public MyFirstClass(){
}
}
以及:
如果我这样做:
var secondClassObject = new MySecondClass(){
FirstClassObject = {membVar1 = 42, membVar3 = "foo"}
};
secondClass是MySecondClass的实例,它有一个MyFirstClass类型的私有成员变量,该变量具有只读属性。但是,我可以更改membVar1和membVar2的状态。没有封装问题吗
致以最良好的祝愿
MySecondClass上的FirstClassObject属性没有setter这一事实并不意味着从getter返回的对象是不可变的。因为它有公共字段,所以这些字段是可变的。因此说
secondClassObject.FirstClassObject.membVar1=42是完全合法的。缺少setter仅意味着您无法将存储在firstClassObject字段中的对象引用替换为对其他对象的引用。请注意:您没有更改MySecondClass.firstClassObject的值。您只需更改该属性内的值
比较以下两个代码段。第一个是合法的,第二个不是,因为它试图为FirstClassObject
属性指定新值:
// legal:
var secondClassObject = new MySecondClass(){
FirstClassObject = {membVar1 = 42, membVar3 = "foo"} }
// won't compile:
// Property or indexer 'FirstClassObject' cannot be assigned to -- it is read only
var secondClassObject = new MySecondClass(){
FirstClassObject = new MyFirstClass {membVar1 = 42, membVar3 = "foo"} }
基本上,您的代码只是一种非常奇特的编写方式:
var secondClassObject = new MySecondClass();
secondClassObject.FirstClassObject.membVar1 = 42;
secondClassObject.FirstClassObject.membVar3 = "foo";
我就是这样写的。它是明确的和可理解的。类型为MyFirstCLass
的存储位置和类型为MyFirstCLass
的属性返回的值都不包含字段membVar1
,membVar2
,等等。存储位置或属性包含的信息足以识别MyFirstCLass
的实例或指示其为“null”。在某些语言或框架中,存在标识对象但只允许对其执行某些操作的引用类型,但Java和.NET都使用混杂的对象引用:如果对象允许持有引用的外部代码对其执行某些操作,则任何获得引用的外部代码都可以这样做
如果一个类正在使用一个可变对象来封装它自己的状态,并且希望允许外部世界看到该状态,但不允许外部世界对其进行篡改,那么它不能直接将该对象返回给外部代码,而是给外部代码一些其他的东西。可能性包括:
- 单独公开对象包含的状态的所有方面(例如,具有返回封装对象的
membVar1
值的membVar1
属性)。这可以避免混淆,但调用者无法将属性作为一个组来处理
- 返回只读包装器的一个新实例,该实例包含对私有对象的引用,并具有将读取请求(但不是写入请求)转发给这些成员的成员。返回的对象将用作只读“视图”,但外部代码无法很好地识别两个这样的对象是否是同一基础对象的视图
- 具有在构造函数中初始化的只读包装类型的字段,并具有返回该字段的属性。如果每个对象只有一个与之关联的只读包装器,则两个包装器引用仅在标识相同包装器时才会查看相同的包装器对象
- 创建基础数据的不可变副本,可能是通过创建新的可变副本并向其返回新的只读包装。这将为调用者提供数据的“快照”,而不是实时的“视图”
- 创建基础数据的新可变副本,并返回该副本。这样做的缺点是,试图通过更改副本来更改基础数据的调用方将被允许在没有任何警告的情况下更改副本,但该操作将不起作用。所有关于可变结构为何是“邪恶”的论点在这里都有双重含义:接收公开字段结构的代码应该期望对接收到的结构的更改不会影响其来源,但接收可变类对象的代码无法知道这一点。财产不应如此行事;这种行为通常只适用于明确其意图的方法(例如,
FirstClassObjectAsNewMyFirstClass();
- 要求调用者传入一个可接受基础数据类型的可变对象,并将数据复制到该对象中。这使调用者可以以可变形式获得数据(在某些情况下可能更易于使用),但同时避免了对谁“拥有”的任何混淆作为一个额外的好处,如果调用者将进行许多查询,调用者可以为所有查询重用相同的可变对象,从而避免不必要的对象分配
- 将数据封装在一个结构中,并让属性返回该结构。有些人可能会反对这种用法,但在调用方可能希望分段修改数据的情况下,这是一种有用的约定。只有当所讨论的数据限于一组固定的离散值时,这种方法才真正起作用(例如矩形的坐标和尺寸),但有一个优势,即如果调用方理解.NET结构是什么(所有.NET程序员都应该理解),那么语义本质上是显而易见的
在这些选择中,只有最后两个通过类型系统明确了调用方应该期望的语义。从调用方接受可变对象提供了明确的语义,但会使使用变得笨拙。返回公开的字段结构提供了明确的语义,但仅当数据由一组固定的离散值组成时。返回可变的数据的拷贝有时是有用的,但只有在方法名明确说明它在做什么时才合适
var secondClassObject = new MySecondClass();
secondClassObject.FirstClassObject.membVar1 = 42;
secondClassObject.FirstClassObject.membVar3 = "foo";