一种非常常见的C#模式,打破了一个非常基本的OOP原则

sam*_*mis 14 c# oop encapsulation design-principles solid-principles

这是一个非常简单的问题,我仍然非常不安:

为什么现在广泛接受类通过访问器方法返回对其私有成员的引用?这不完全打破封装原则吗?如果这样可以,那为什么不公开会员!?

public class EncapsulationViolator
{
  private object abuseMe;

  public object AbuseMe 
  {
    get { return abuseMe; }
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑我正在考虑的案例就是这个

EncapsulationViolator ev = new EncapsulationViolator();

object o = ev.AbuseMe;

o.SetValue(newValue);
Run Code Online (Sandbox Code Playgroud)

现在,ev的状态已经通过传递性而发生变化,因为其成员滥用状态已发生变化.

在DDD的上下文中,如果对象是聚合根,则不行.我引用

允许外部对象仅保留对根的引用.可以传递对内部成员的瞬时引用,以便仅在单个操作中使用.因为根控制访问权限,所以不能通过对内部的更改来愚蠢.

[ 领域驱动设计,Eric Evans]

... setters schmetters ......

use*_*116 12

您将C++术语"引用"与C#按值(它们的引用)传递对象的事实混为一谈.

在这种情况下,getter的调用者AbuseMe不能交换私有字段abuseMe.因此,没有违反封装.

EncapsulationViolator x = new EncapsulationViolator();
object y = x.AbuseMe;
y = 17; // I have not changed x.AbuseMe

Debug.Assert(y != x.AbuseMe); // Passes!
Run Code Online (Sandbox Code Playgroud)

此外,属性getter和setter允许私有字段的正确封装,并且在功能上与将它们实现为方法相同(实际上它们由编译器实现为方法).

返回一个私有变量可能会破坏封装的一种情况是当您返回对数组的引用时:

class X
{
    private int[] amazing = new int[10];

    public int[] Amazing { get { return this.amazing; } }
}

X a = new X();
int[] x = a.Amazing;
int[] y = a.Amazing;

x[2] = 9;
Debug.Assert(x[2] != y[2]); // Fails!
Run Code Online (Sandbox Code Playgroud)


Guf*_*ffa 10

这取决于成员是什么类型的对象.如果它是一个字符串,那么它是不可变的,所以你不能改变字符串.

如果它是一个可变对象,您可以从类外部更改对象的内容,但不能替换该对象本身.

如果对象不可能从类外部更改,则getter应返回该对象的不可变版本.

如果你做错了,模式可能会打破封装,但正确完成封装是完整的.


Bot*_*000 8

我不认为它破坏了封装.该课程仍然决定返回值的AbuseMe来源.它可能来自不同的成员,也可能每次都可以重新创建或复制.

关键是该类决定它允许用户使用该成员做什么(获取/设置或两者及其可见性),它可以执行验证并防止设置无效值,并且用户不需要知道该值的位置来自.

此外,如果要将自定义逻辑添加到get/set方法,则可以在不破坏与其他程序集的兼容性的情况下执行此操作.