241

我试图了解 C# 中的嵌套类。我知道嵌套类是在另一个类中定义的类,我不明白为什么我需要这样做。

4

8 回答 8

284

我特别喜欢的一种模式是将嵌套类与工厂模式结合起来:

public abstract class BankAccount
{
  private BankAccount() {} // prevent third-party subclassing.
  private sealed class SavingsAccount : BankAccount { ... }
  private sealed class ChequingAccount : BankAccount { ... }
  public static BankAccount MakeSavingAccount() { ... }
  public static BankAccount MakeChequingAccount() { ... }
}

通过像这样嵌套类,我使第三方无法创建自己的子类。我可以完全控制在任何银行账户对象中运行的所有代码。我所有的子类都可以通过基类共享实现细节。

于 2009-07-05T15:20:18.277 回答
124

目的通常只是限制嵌套类的范围。与普通类相比,嵌套类具有private修饰符的额外可能性(protected当然也是如此)。

基本上,如果您只需要在“父”类中使用这个类(就范围而言),那么通常将其定义为嵌套类是合适的。如果这个类可能需要在没有程序集/库的情况下使用,那么用户通常更方便地将其定义为一个单独的(兄弟)类,无论这两个类之间是否存在任何概念关系。尽管在技术上可以创建一个public嵌套在public父类中的类,但在我看来,这很少是一个合适的实现方式。

于 2009-07-04T21:51:03.640 回答
53

嵌套类可以具有private和访问修饰符以及protected和。protected internalpublicinternal

例如,您正在实现GetEnumerator()返回IEnumerator<T>对象的方法。消费者不会关心对象的实际类型。他们所知道的只是它实现了该接口。您要返回的类没有任何直接用途。您可以将该类声明为private嵌套类并返回它的一个实例(这实际上是 C# 编译器实现迭代器的方式):

class MyUselessList : IEnumerable<int> {
    // ...
    private List<int> internalList;
    private class UselessListEnumerator : IEnumerator<int> {
        private MyUselessList obj;
        public UselessListEnumerator(MyUselessList o) {
           obj = o;
        }
        private int currentIndex = -1;
        public int Current {
           get { return obj.internalList[currentIndex]; }
        }
        public bool MoveNext() { 
           return ++currentIndex < obj.internalList.Count;
        }
    }
    public IEnumerator<int> GetEnumerator() {
        return new UselessListEnumerator(this);
    }
}
于 2009-07-04T21:52:08.537 回答
49

我不明白为什么我需要这样做

我认为你永远不需要这样做。给定一个像这样的嵌套类......

class A
{
  //B is used to help implement A
  class B
  {
    ...etc...
  }
  ...etc...
}

...您始终可以将内部/嵌套类移动到全局范围,就像这样...

class A
{
  ...etc...
}

//B is used to help implement A
class B
{
  ...etc...
}

但是,当 B 仅用于帮助实现 A 时,使 B 成为内部/嵌套类有两个优点:

  • 它不会污染全局范围(例如,可以看到 A 的客户端代码不知道 B 类甚至存在)
  • B 的方法隐式地可以访问 A 的私有成员;而如果 B 没有嵌套在 A 中,则 B 将无法访问 A 的成员,除非这些成员是内部的或公共的;但随后将这些成员设为内部或公共也会将它们暴露给其他类(不仅仅是 B);因此,将 A 的这些方法保留为私有,并通过将 B 声明为嵌套类来让 B 访问它们。如果您了解 C++,这就像说在 C# 中所有嵌套类都自动成为它们所在类的“朋友”(并且,将类声明为嵌套类是在 C# 中声明友谊的唯一方法,因为C# 没有friend关键字)。

当我说 B 可以访问 A 的私有成员时,这是假设 B 具有对 A 的引用;它经常这样做,因为嵌套类通常是这样声明的......

class A
{
  //used to help implement A
  class B
  {
    A m_a;
    internal B(A a) { m_a = a; }
    ...methods of B can access private members of the m_a instance...
  }
  ...etc...
}

...并使用这样的代码从 A 的方法构造...

//create an instance of B, whose implementation can access members of self
B b = new B(this);

您可以在 Mehrdad 的回复中看到一个示例。

于 2009-07-05T01:51:44.640 回答
35

公共嵌套成员也有很好的用途......

嵌套类可以访问外部类的私有成员。因此,在创建比较器(即实现 IComparer 接口)时,这是正确方法的一种情况。

在此示例中, FirstNameComparer 可以访问私有 _firstName 成员,如果该类是单独的类,则不会...

public class Person
{
    private string _firstName;
    private string _lastName;
    private DateTime _birthday;

    //...
    
    public class FirstNameComparer : IComparer<Person>
    {
        public int Compare(Person x, Person y)
        {
            return x._firstName.CompareTo(y._firstName);
        }
    }
}
于 2009-07-05T02:15:35.497 回答
20

有时实现一个从类中返回的接口很有用,但该接口的实现应该对外界完全隐藏。

举个例子——在向 C# 添加 yield 之前,实现枚举器的一种方法是将枚举器的实现作为私有类放在集合中。这将提供对集合成员的轻松访问,但外部世界不需要/查看如何实现的细节。

于 2009-07-04T21:52:38.740 回答
6

嵌套类对于实现不应暴露的内部细节非常有用。如果您使用 Reflector 检查 Dictionary<Tkey,TValue> 或 Hashtable 等类,您会发现一些示例。

于 2009-07-04T22:48:23.010 回答
4

也许这是什么时候使用嵌套类的一个很好的例子?

// ORIGINAL
class ImageCacheSettings { }
class ImageCacheEntry { }
class ImageCache
{
    ImageCacheSettings mSettings;
    List<ImageCacheEntry> mEntries;
}

和:

// REFACTORED
class ImageCache
{
    Settings mSettings;
    List<Entry> mEntries;

    class Settings {}
    class Entry {}
}

PS:我没有考虑应该应用哪些访问修饰符(私有、受保护、公共、内部)

于 2011-12-31T14:36:54.627 回答