C#中的私有内部类-为什么它们不经常使用?


76

我是C#的新手,每次我开始从事C#项目时(我只从事C#中几乎成熟的项目),我想知道为什么没有内部类吗?

也许我不明白他们的目标。对我而言,内部类(至少是私有内部类)在很大程度上类似于Pascal / Modula-2 / Ada中的“内部过程”:为了简化理解,它们允许将主类分解为较小的部分。

示例:这是大多数时候看到的内容:

public class ClassA
{
   public MethodA()
   {
      <some code>
      myObjectClassB.DoSomething(); // ClassB is only used by ClassA
      <some code>
   }
}

public class ClassB
{
   public DoSomething()
   {
   }
}

由于ClassB仅(至少一段时间)将由ClassA使用,因此我的猜测是此代码可以更好地表示为:

   public class ClassA
   {
      public MethodA()
      {
         <some code>
         myObjectClassB.DoSomething(); // Class B is only usable by ClassA
         <some code>
      }

      private class ClassB
      {
         public DoSomething()
         {
         }
      }
   }

我很高兴收到您关于这个问题的消息-对吗?


1
另外请注意,你不能在嵌套类扩展方法..
nawfal

Answers:


80

嵌套类(可能最好避免使用“内部”一词,因为C#中的嵌套类与Java中的内部类有所不同)确实可以非常有用。

尚未提及的一种模式是“更好的枚举”模式-甚至比Java中的模式更灵活:

public abstract class MyCleverEnum
{
    public static readonly MyCleverEnum First = new FirstCleverEnum();
    public static readonly MyCleverEnum Second = new SecondCleverEnum();

    // Can only be called by this type *and nested types*
    private MyCleverEnum()
    {
    }

    public abstract void SomeMethod();
    public abstract void AnotherMethod();

    private class FirstCleverEnum : MyCleverEnum
    {
        public override void SomeMethod()
        {
             // First-specific behaviour here
        }

        public override void AnotherMethod()
        {
             // First-specific behaviour here
        }
    }

    private class SecondCleverEnum : MyCleverEnum
    {
        public override void SomeMethod()
        {
             // Second-specific behaviour here
        }

        public override void AnotherMethod()
        {
             // Second-specific behaviour here
        }
    }
}

我们可以使用某种语言支持来自动完成其中的一些操作-而且这里没有显示很多选项,例如实际上没有为所有值使用嵌套类,或者为多个值使用相同的嵌套类,但给他们不同的构造函数参数。但基本上,嵌套类可以调用私有构造函数的事实赋予了很多功能。


11
非常聪明!但是我永远不会使用它。软件书籍的作者和他们的读者(!)通常都是聪明人。他们认为很多有趣的事情都很酷。但是软件维护者是不阅读Gamma / Skeet的普通人。我的客人:它越聪明,就
越难

29
直到它成为公认的模式,这都是事实。就像“ while((line = streamReader.ReadLine())!= null)”一开始看起来就很糟糕-处于条件状态的副作用!但是,当它成为惯用语言时,您会越过该气味并欣赏其简洁性。
乔恩·斯基特

2
@乔恩,谢谢!我想我明白了。MyCleverEnum既充当枚举成员的持有者,又充当每个枚举成员的接口,因此它比其他解决方案更简洁。嵌入式类的使用确保了支持类对类而言是隐藏的。私有构造函数使类从类外部像静态/单例类,但从类内部像抽象基类一样运行。
devuxer

1
使用这样的类作用域发现我讨厌的一件事是,公共内部类最终都带有“点缀”的公共名称。是否有任何干净的方法可以使可公共访问的类具有与嵌套类相同的作用域,但可以通过“简单”名称进行公共访问?
supercat

1
@supercat,您可以在文件顶部放置一个using指令using FirstCleverEnum = MyCleverEnum.FirstCleverEnum。这样可以缓解问题,但是无法“导出”该别名。
马蒂·尼尔

31

到目前为止,《框架设计指南》为使用嵌套类提供了最佳规则。

这是一个简短的摘要列表:

  1. 当类型和嵌套类型之间的关系需要成员可访问性语义时,请使用嵌套类型。

  2. 千万不要使用公共嵌套类型的逻辑组结构

  3. 避免使用公开的嵌套类型。

  4. 千万不要,如果类型很可能被引用包含类型之外使用嵌套类型。

  5. 千万不要,如果他们需要通过客户机代码实例化使用嵌套类型。

  6. NOT限定嵌套类型作为接口的成员。


1
其中一些是相当明显的(例如4和5)。但是您能指出为什么推荐其他规则的原因吗?
Peter Bagnall 2014年

@PeterBagnall是我还是第六名与接受的答案矛盾?
jungle_mole

@jungle_mole实现公共接口的嵌套类型很好并且很常见。嵌套类型类型永远不应公开。
埃里克

@Erick是的。我只是在脑海中交换了两个“设计”的两个部分。原因很明显。我什至在想什么
-jungle_mole

12

您应该限制每个类的职责,以使每个类保持简单,可测试和可重用。私人内部阶级与此相反。它们导致外部类的复杂性,它们不可测试,并且不可重用。


简单,可测试和可重用非常好。但是封装呢?当不应在外面看到东西时,为什么要公开?在我的书中,内部类可以使整个过程更简单。而且它们都是可测试的(具有放松功能)!
Sylvain Rodrigue

4
我会选择封装作为反对内部类的参数。内部类可以访问其外部类的私有成员!
Wim Coenen

你的权利 !有一个内部类在它和外部类之间增加了某种耦合-最初我没有看到它-抱歉。谢谢!
Sylvain Rodrigue

9
我看到程序员一直在使用诸如元组之类的数据结构,而不是私有类。我肯定会争辩说,私有类会导致代码更易于阅读且不易出错。认为它违反了封装是不正确的。私有类没有违反封装的任何内容。它包含在类的内部,并且应该并且确实具有外部类的任何其他成员的所有权利。
2014年

与一切一样,这取决于。在这种情况下,我认为这取决于嵌套类的具体情况。例如,如果您在父类中创建一个dto(例如,保持方法签名清洁),则无需进行任何测试。与创建私有Tuples imo没什么不同,只是更加清楚。但是,如果您创建具有功能的丰富嵌套类,那么您将度过一个糟糕的时光。
Sinaesthetic

3

就我个人而言,仅在需要创建对象的进程内集合(可能需要在其上使用方法)的情况下,才创建私有内部类。

否则,可能会使其他从事该项目的开发人员实际上找不到这些类,这会引起混乱,因为他们不清楚它们在哪里。


1
但是,由于它们是私有类,因此其他开发人员不应该意识到它们,除非他们必须维护外部类。当然,这在人们不了解私有内部类的情况下可能是个问题。感谢您的回答 !
Sylvain Rodrigue

16
如果那使我的开发人员感到困惑,我会得到新的开发人员。
DancesWithBamboo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.