【问题标题】:C# and Data HidingC# 和数据隐藏
【发布时间】:2008-11-11 05:42:00
【问题描述】:

我通常是 C# 和 .Net 的新手,所以这可能是一个幼稚的问题。但无论如何,请考虑以下 C# 代码:

class A {
    public int Data {get; set;}
}

class B {
    public A Aval {get; set;}
}

上面的 B.Aval 属性返回对其内部 A 对象的引用。作为一名前 C++ 程序员,我发现这很危险,因为公开对成员对象类 B 的引用违反了数据隐藏原则。尽管如此,这似乎是 C# 世界的正常做法。

我的问题是,如果有的话,为什么这样的设计是通常的方法,而不是返回内部成员的副本,这在很多方面会更安全(包括线程安全的情况)?

我知道,即使在 C++ 中,有时好的设计也要求您直接公开类成员(具有 EngineStereo 对象的 Car 类想到里面),但这并不是 C# 中的规范。

【问题讨论】:

  • 您的意思是“上面的 B.Aval 属性正在返回对其内部 A 对象的引用”而不是“... B 对象”?
  • 在 C++ 中,您通常会编写 B.Aval 的 get 方法,使其复制其内部 A,然后返回该副本吗?
  • 没错。在 C++ 中,在 90% 的情况下,get 方法将返回成员变量的副本,而不是引用(或指针)。
  • 我想我的意思是您的代码 sn-p 没有说明 C# 中的封装。通常,您要么包装 & 不直接公开内部实例,要么继承 & 可选地覆盖以在 C# 中执行封装。
  • 我明白,Steph,但我的问题是,为什么 C# 程序员似乎不关心封装。在 C++ 世界中,封装(即通过复制返回等等)始终是默认设计。对于 C#,为什么返回内部引用是默认方法,尽管它不太安全?

标签: c# .net


【解决方案1】:

您是绝对正确的 - 您应该只从对象不可变的属性中返回对象,或者您很高兴调用者可以对其进行任何程度的修改。一个典型的例子是返回集合——通常返回一个集合的只读包装器比直接返回“真正的”集合要好得多。

另一方面,实用主义有时要求仅将其记录为“请不要更改返回的对象” - 特别是当它是仅在公司内部使用的 API 时。

希望在未来的 C# 和 .NET 版本中能够对不变性提供更多支持,这将使这更容易解决 - 但在许多情况下它可能仍然是一个棘手的问题。

【讨论】:

    【解决方案2】:

    这不是封装 - 它是通过对象组合或聚合进行的抽象行为,具体取决于内部对象生命周期的创建/管理方式。

    在组合模式中,访问组合状态是完全可以接受的,例如B 的实例中的 A 的实例。

    http://en.wikipedia.org/wiki/Object_composition

    正如您所指出的,封装的语义非常不同 - 完全隐藏 A 的内部实现,例如通过从 A 继承 B。

    【讨论】:

    • 我不认为组合cmets的想法在内部成员的访问级别上。它只是说 B 类与 A 类具有“具有”关系。仅此而已。
    • 此外,封装与从 A 继承 B 无关。它只是从客户端代码中隐藏类的内部细节(例如其数据成员)。无论您是继承还是使用组合,还是与封装无关的任何东西。
    【解决方案3】:

    这可能与Law of Demeter...有关,您是在谈论没有额外逻辑(因此有效地直接暴露成员)的getter 和setter,还是任何get-set 对?无论如何,如果外部对象的这部分状态不参与外部对象的有意义的不变量,我认为这样做不一定是不合理的。

    【讨论】:

    • 完全同意——Demeter 完全涵盖了抽象——在这种情况下,你会给出包含 A 的 B 的“整个玩具”组合来玩。如果您不想让它们与 A 混淆,则应使用封装而不是抽象。
    猜你喜欢
    • 2013-03-25
    • 2010-09-17
    • 2010-11-13
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多