人们想要使用嵌套类的原因是啥? [复制]

Posted

技术标签:

【中文标题】人们想要使用嵌套类的原因是啥? [复制]【英文标题】:What are reasons why one would want to use nested classes? [duplicate]人们想要使用嵌套类的原因是什么? [复制] 【发布时间】:2010-07-21 14:01:14 【问题描述】:

在this *** answer 中,一位评论者提到“私有嵌套类”可能非常有用,所以我在articles such as this one 中阅读了它们,它倾向于解释嵌套类的功能技术上,但不是为什么你会使用它们。

我想我会为属于较大类的小助手类使用私有嵌套类,但通常我需要另一个类的助手类,所以我只需要采取额外的努力 (1) 使嵌套类成为非嵌套类或 (2) 将其公开,然后使用外部类前缀访问它,这似乎都是额外的工作,没有任何附加值让嵌套类在第一名。因此,总的来说我真的没有看到嵌套类的用例,除了可能让类更组织成组,但我也违反了one-class-我开始享受每个文件的清晰度。

您通过哪些方式使用嵌套类来使您的代码更易于管理、更易读、更高效?

【问题讨论】:

嵌套类的用例是为了确保 Eric Lippert 与编译器相关的博文中讨论的问题不像乍一看那么简单...... 一个非常好的问题,但鉴于它没有明确的答案,它会受益于成为社区 wiki。 【参考方案1】:

您已经回答了自己的问题。当你需要一个在类外没有意义的辅助类时,使用嵌套类;特别是当嵌套类可以利用外部类的私有实现细节时。

您关于嵌套类无用的论点也是私有方法无用的论点:私有方法可能在类之外有用,因此您必须将其设为内部。内部方法可能在程序集之外很有用,因此您可以将其公开。因此,所有方法都应该是公开的。如果您认为这是一个不好的论点,那么您为类而不是方法提出相同的论点有什么不同?

我一直在制作嵌套类,因为我经常需要将功能封装在一个在类之外没有意义的帮助器中,并且可以使用外部类的私有实现细节。例如,我编写编译器。我最近写了一个类 SemanticAnalyzer,它对解析树进行语义分析。它的嵌套类之一是 LocalScopeBuilder。当我分析解析树的语义时,在什么情况下需要构建本地范围?绝不。该类完全是语义分析器的实现细节。我计划添加更多嵌套类,其名称如 NullableArithmeticAnalyzer 和 OverloadResolutionAnalyzer,它们在类之外也无用,但我想将语言规则封装在这些特定类中。

人们还使用嵌套类来构建诸如迭代器或比较器之类的东西——这些东西在类之外毫无意义,并通过众所周知的接口公开。

我经常使用的一种模式是让私有嵌套类扩展其外部类:

abstract public class BankAccount

    private BankAccount()  
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount  ... 
    public static BankAccount MakeChequingAccount()  return new ChequingAccount(); 
    private sealed class SavingsAccount : BankAccount  ... 

等等。嵌套类非常适合工厂模式。这里的BankAccount 是各类银行账户的工厂,所有这些都可以使用BankAccount 的私有实现细节。但是没有第三方可以创建自己的扩展 BankAccount 的类型 EvilBankAccount。

【讨论】:

+1 类/方法比较和 API 示例当然都有意义 我认为我从未意识到嵌套类的价值的一个原因是,正如 Henk 在这里指出的那样,不是 嵌套类 本身有用,而是 私有嵌套类。例如,您在答案中使用的所有示例都是私有嵌套类。因此,在阅读了上述问题的所有答案后,我现在可以为私有嵌套类列举一些好的用例,但对于公共、内部或公共内部嵌套类则没有。 @Edward:确实,框架设计指南表明公共嵌套类是个坏主意。合法,但风格不好。有点像公共领域。您需要公共嵌套类的情况很少见。 是的,混淆部分来自于教程,教如何制作私有和公共嵌套类,就好像它们在您的编程中具有同等价值 @Edward:那将是糟糕的教程。教程应该向您展示如何正确使用技术!仅供参考,我添加了一些代码,显示了私有嵌套类的特别好的模式。【参考方案2】:

向要隐藏其实现的调用者返回一个接口。

public class Outer

    private class Inner : IEnumerable<Foo>
    
        /* Presumably this class contains some functionality which Outer needs
         * to access, but which shouldn't be visible to callers 
         */
    

    public IEnumerable<Foo> GetFoos()
    
        return new Inner();
    

【讨论】:

【参考方案3】:

私有助手类就是一个很好的例子。

例如,后台线程的状态对象。没有令人信服的理由公开这些类型。将它们定义为私有嵌套类型似乎是一种非常简洁的处理方式。

【讨论】:

没有什么比打开一个包对某些代码进行维护并看到 50 个类(其中一半是 *XMLMarshaler 和 *Helper)更糟糕的了。 :-( 是的。我使用私有嵌套类型的最常见方式是处理表单中的状态转换。这样做的好处是状态可以访问和修改表单控件,而不必将它们设为公共/内部,并且它将实际的状态转换细节与主表单代码分开。【参考方案4】:

当两个绑定值(例如在哈希表中)在内部不够用但在外部足够用时,我会使用它们。然后我用我需要存储的属性创建一个嵌套类,并通过方法只公开其中的几个。

我认为这是有道理的,因为如果没有其他人会使用它,为什么要为它创建一个外部类?这样做是没有意义的。

对于每个文件一个类,你可以使用partial关键字创建部分类,这是我通常做的。

【讨论】:

+1 从没想过只为嵌套类使用 partial,有趣【参考方案5】:

我最近遇到的一个引人注目的例子是许多数据结构的Node 类。例如,Quadtree 需要知道它如何在其节点中存储数据,但代码的其他部分不应该关心。

【讨论】:

【参考方案6】:

我发现了一些非常方便的案例:

    管理复杂的私有状态,例如 Interpolator 类使用的 InterpolationTriangle。 Interpolator 的用户不需要知道它是使用 Delauney 三角剖分实现的,当然也不需要知道三角形,因此数据结构是私有嵌套类。

    正如其他人所提到的,您可以通过接口公开类使用的数据,而无需透露类的完整实现。嵌套类还可以访问外部类的私有状态,这使您可以编写紧密耦合的代码,而无需公开(甚至在内部对程序集的其余部分)公开该紧密耦合。

    我遇到过一些情况,其中框架希望类派生自某个基类(例如 WPF 中的 DependencyObject),但您希望您的类继承自不同的基类。通过使用继承自框架基类的私有嵌套类,可以与框架进行互操作。因为嵌套类可以访问私有状态(创建时只需传递父级的'this'),基本上可以使用this通过组合实现穷人的多重继承。

【讨论】:

+1 你的第三点关于穷人的多重继承很深刻,值得尝试【参考方案7】:

使用此技术的一个非常常见的模式是,类从其属性或方法之一返回接口或基类类型,但具体类型是私有嵌套类。请考虑以下示例。

public class MyCollection : IEnumerable

  public IEnumerator GetEnumerator()
  
    return new MyEnumerator();
  

  private class MyEnumerator
  
  

【讨论】:

【参考方案8】:

我认为其他人已经很好地涵盖了公共和私有嵌套类的用例。

我没有看到的一点是您对 one-class-per-file 的担忧。您可以通过将外部类设为部分来解决此问题,并将内部类定义移动到单独的文件中。

OuterClass.cs:

namespace MyNameSpace

    public partial class OuterClass
    
        // main class members here
        // can use inner class
    

OuterClass.Inner.cs:

namespace MyNameSpace

    public partial class OuterClass
    
        private class Inner
        
            // inner class members here
        
    

您甚至可以利用 Visual Studio 的项嵌套使 OuterClass.Inner.cs 成为 OuterClass.cs 的“子级”,以避免弄乱您的解决方案资源管理器。

【讨论】:

【参考方案9】:

当我在某些情况下需要结合 SRP(单一责任主体)时,我通常会这样做。

“好吧,如果 SRP 是你的目标,为什么不将它们分成不同的类?”你会在 80% 的时间里这样做,但是如果你创建的类对你没有用处呢?外面的世界?您不希望只有您自己会使用的类会弄乱您的程序集的 API。

“嗯,这不是internal 的用途吗?” 当然。对于这些情况中的大约 80%。但是必须访问或修改公共类状态的内部类呢?例如,该类被分解为一个或多个内部类以满足您的 SRP 连续性?您还必须将这些internal 类使用的所有方法和属性标记为internal

“这有什么问题?”什么都没有。对于这些情况中的大约 80%。当然,现在您正在使用仅对您之前创建的那些类有用的方法/属性来弄乱类的内部接口。现在您必须担心团队中的其他人编写内部代码不会以您未曾预料到的方式使用这些方法来搞乱您的状态。

内部类可以修改定义它们的类型的任何实例的状态。因此,无需在类型定义中添加成员,您的内部类就可以根据需要对它们进行处理。在 100 种情况下,大约有 14 种情况是保持类型整洁、代码可靠/可维护以及职责单一的最佳选择。

【讨论】:

【参考方案10】:

作为一个例子,它们非常适合 singleton pattern. 的实现

我也有几个地方使用它们来“增加”价值。我有一个多选组合框,我的内部类也存储复选框的状态和数据项。世界不需要知道/使用这个内部类。

【讨论】:

【参考方案11】:

私有匿名嵌套类对于 GUI 中的事件处理程序至关重要。

如果某个类不是另一个类导出的 API 的一部分,则必须将其设为私有。否则你暴露的比你想要的多。 “百万美元的漏洞”就是一个例子。大多数程序员对此都束手无策。

彼得

【讨论】:

有意思,你指的是什么百万美元的bug,找不到参考 “专家 C 编程”第 125 页的故事。我错认为这是 $1.0E6 错误 - 这是另一个错误。【参考方案12】:

这个问题被标记为 C#,所以我不确定这是否有趣,但在 COM 中,当 C++ 类实现多个 COM 接口时,您可以使用内部类来实现接口......本质上,您将它用于组合而不是多个-继承。

此外,在 MFC 和其他技术中,您可能需要您的控件/对话框具有放置目标类,除了作为嵌套类之外,这几乎没有意义。

【讨论】:

【参考方案13】:

如果一个对象需要返回一些关于其状态的抽象信息,私有嵌套类可能是合适的。例如,如果 Fnord 支持“保存上下文”和“恢复上下文”方法,让“保存上下文”函数返回 Fnord.SavedContext 类型的对象可能很有用。类型访问规则并不总是最有用的;例如,似乎很难让 Fnord 访问 Fnord.SavedContext 的属性和方法,而不使这些属性和方法对外部人员可见。另一方面,可以让 Fnord.CreateSaveContext 简单地创建一个以 Fnord 作为参数的 New Fnord.SaveContext(因为 Fnord.SaveContext 可以访问 Fnord 的内部),并且 Fnord.LoadContextFrom() 可以调用 Fnord.SaveContext.RestoreContextTo ()。

【讨论】:

以上是关于人们想要使用嵌套类的原因是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

在 NHibernate 中映射嵌套类的 XML 语法是啥

静态类和内部类的区别是啥

父类的复制构造函数中的嵌套类缺少默认构造函数

为啥编译包含静态嵌套类的类会创建一个名为“EnclosureClass$1”的新 .class 文件? [复制]

不允许嵌套类的前向声明的原因?

java 嵌套类