【问题标题】:Why can private member variable be changed by class instance?为什么类实例可以更改私有成员变量?
【发布时间】:2011-05-13 18:56:20
【问题描述】:
class TestClass
{
    private string _privateString = "hello";
    void ChangeData()
    {
        TestClass otherTestClass = new TestClass();
        otherTestClass._privateString = "world";
    }
}

这段代码在 C# 中编译,而在 PHP 中的等效代码,但是有人可以解释为什么 otherTestClass._privateString 可以在这里更改吗?

我原以为一个类的实例在任何情况下都不能更改私有成员变量,并且尝试访问 otherTestClass._privateString 会给出“由于保护级别而无法访问”错误。

但情况并非如此,那么为什么在其自己的类中实例化一个对象可以让您访问私有成员?而且,这不是在一定程度上破坏了封装吗?还是我遗漏了一些明显的东西?

  • (我不是问上面的类设计是否是好的实践,只是想知道它背后的理论。)

编辑 - 感谢您的回答和 cmets。澄清一下,我也很想知道能够做到这一点是否被认为是一个积极的特性,或者它是否是更好的编译时检查/代码清晰度的必要权衡/因为大多数其他语言都是这样做的或其他方式。在我看来,理想情况下,编译器会阻止或警告你,但我离语言设计师还很远。任何关于这种方式的示例都可以让您做一些有用的事情(不违反封装),否则会很困难或不可能。

【问题讨论】:

  • 顺便说一句:你可以创建ChangeData() 甚至static,你仍然可以访问私人成员。
  • @ulrichb - 重要提示 OP,如果 ChangeData()static,则不能使用 this 关键字访问私有成员。
  • 作为一种不同语言的示例 - 在 Ruby 中,public 是相同的,但 private 表示“只能由同一个实例访问”,而 protected 表示“只能从同一个类访问”。继承与那里无关。

标签: c# oop encapsulation instantiation private-members


【解决方案1】:

该类的程序文本中的任何代码(包括嵌套类型)都可以访问私有成员。它与您正在处理的类的哪个实例无关。

我不认为这违反了封装 - API 仍然与实现分离,但实现“了解”自身,无论它查看的是哪个实例。

我相信在其他一些语言中,这不是可访问性的工作原理,但它绝对适用于 C# 和 Java。 (Java 关于什么可以访问私有成员的规则略有不同,但您编写的翻译后的代码仍然可以工作。)

【讨论】:

  • 在实例级别实现可访问性需要对每个成员变量访问进行运行时检查,而此模型可以在编译时检查。
  • @James:嗯,不一定。您可以要求私有成员只能通过“this”引用获得。这会很可怕,但有可能。
  • 我想在 Jon 的回答中补充一点,这种类型的访问控制允许轻松编写诸如相等之类的 n 元运算符。如果两个实例都无法访问其他私有成员,则实现相等运算符将更加困难。
  • 我同意 Andy 的观点,但仍然认为类级别的隐私与对象级别的隐私相比存在一些弱点。这就是当我在 java 中实现 equals(Object other) 时,我使用 getter 来获取“其他”对象的属性的原因。显然这并不总是可能的:(
  • @miket2e:好的,一个非常简单的例子:它允许你使用私有成员在两个对象之间进行比较。基本上我看不出它会造成伤害,而且它在语言复杂性、编译器工作和语言使用方面让生活更轻松。
【解决方案2】:

这是因为 C# 强制执行类级别的隐私,而不是对象级别的隐私。

大多数主流语言都执行相同的策略,即 C#、C++ 和 Java。我认为原因是:

1) 因为开发者已经习惯了这种政策;

2) 因为对象级别的隐私会变得过于乏味,而优势却很少。

【讨论】:

  • 我认为不需要使用“this”,因为编译器确实能够区分成员变量。例如,我认为需要为每个类的成员值设置 getter 方法会很乏味。事实上,它会违反封装,因为您也会将私有成员暴露给其他类!
  • @miket2e 尝试实现一个复制构造函数或一个简单的具有对象级隐私的 Equals 函数。在当前的 C# 中很简单:return this._somePrivateField.Equals(other._somePrivateField) - 如果您可以访问 other._somePrivateField,您将如何在不添加公共 getter 的情况下实现这样的功能?
  • @MichaelStum:将字段设为实例私有或实例保护的一个优点是,这样做可以清楚地表明派生类可以在不违反 Liskov 替换原则的情况下重新利用这些字段。否则,这种重新利用可能会导致 LSP 违规。像Equals 这样的东西可以通过拥有一个类私有Equals 方法来实现,该方法接受另一个对象的内容(作为一堆离散参数,或使用类保护类型),并拥有公共@987654323一个对象的@方法将其状态传递给另一个对象的Protected等于方法。
猜你喜欢
  • 1970-01-01
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 2011-09-02
  • 2018-12-02
  • 1970-01-01
  • 2012-02-08
相关资源
最近更新 更多