25

这个 stackoverflow 的回答中,一位评论者提到“私有嵌套类”可能非常有用,所以我在诸如这篇文章之类的文章中阅读了它们,这些文章倾向于解释嵌套类在技术上的功能,而不是为什么要使用它们。

我想我会为属于较大类的小助手类使用私有嵌套类,但通常我需要另一个类的助手类,所以我只需要付出额外的努力来(1)使嵌套类不-nested 或 (2) 将其公开,然后使用外部类前缀对其进行访问,这似乎都是额外的工作,而没有任何附加值首先具有嵌套类。因此,总的来说,我真的看不到嵌套类的用例,除了可能使类更加组织成组,但这也违背了我开始享受的每个文件一个类的清晰度.

您以何种方式使用嵌套类来使您的代码更易于管理、更易读、更高效?

4

13 回答 13

31

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

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

我一直在制作嵌套类,因为我经常需要将功能封装在一个在类之外没有意义的帮助器中,并且可以使用外部类的私有实现细节。例如,我编写编译器。我最近写了一个类 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。

于 2010-07-21T14:11:27.343 回答
11

将接口返回给要隐藏其实现的调用者。

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();
    }
}
于 2010-07-21T14:07:50.360 回答
7

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

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

于 2010-07-21T14:03:49.060 回答
6

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

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

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

于 2010-07-21T14:04:24.733 回答
6

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

于 2010-07-21T14:08:54.893 回答
5

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

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

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

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

于 2010-07-21T14:17:23.593 回答
3

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

public class MyCollection : IEnumerable
{
  public IEnumerator GetEnumerator()
  {
    return new MyEnumerator();
  }

  private class MyEnumerator
  {
  }
}
于 2010-07-21T14:08:19.073 回答
3

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

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

外部类.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 的“孩子”,以避免弄乱您的解决方案资源管理器。

于 2010-07-21T21:30:47.557 回答
2

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

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

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

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

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

于 2010-07-21T15:47:27.977 回答
1

例如,它们非常适合单例模式的实现。

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

于 2010-07-21T14:05:30.283 回答
0

私有匿名嵌套类对于 GUI 中的事件处理程序是必不可少的。

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

彼得

于 2010-07-21T14:12:42.630 回答
0

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

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

于 2010-07-21T14:59:46.887 回答
0

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

于 2010-07-21T15:56:39.000 回答