C#,是否可以将嵌套类用于逻辑结构?

时间:2011-09-13 13:40:27

标签: c# design-patterns

我对使用嵌套类有一些争论。情况是类名在两个或多个地方重复是有意义的,虽然每个不同实例之间存在中度相似性,但它们通常是不同的。嵌套类通常不需要(如果有的话)超出其父类的范围。

那么,这不仅仅是提出三个不同的类名,这对我来说似乎更有意义。

class A {
   class B {
}

class M {
   class B {
   }
}

class Q {
   class B {
   }
}

明显的问题不是功能,而是一致性/重复性。我想知道其他开发人员是否曾经遇到过同样的问题,以及一些意见是什么。

11 个答案:

答案 0 :(得分:11)

.net Design Guide advises against it

  • “不要将公共嵌套类型用作逻辑分组构造;请使用名称空间。”
  • “避免公开暴露的嵌套类型。唯一的例外是嵌套类型的变量需要在少数情况下声明,例如子类化或其他高级自定义方案。”

这也是基类库的作用:在System.Web.UI命名空间中,您有DataGridItemDataListItemListViewItemMenuItem,{{1所有这些都可以被称为RepeaterItem并嵌套在ItemDataGrid等内。但是,这会违反上述两个原则。

答案 1 :(得分:1)

如果类B在每个内部类实例之间有任何相似之处,那么您是否有必要将B的相似性抽象为与A一起存在的基类,{{ 1}}和M? (我想是的。)然后你的内部课程,虽然它们可能有相同的名字,但会更清洁。

话虽如此,在MVC应用程序中可以看到这种类型的结构,例如元数据。在那种情况下,你会有类似的东西:

Q

在这些情况下,内部类每个都有相同的用途,但它们的实现因每个父类而异。此外,对于此处的[MetadataType(typeof(A.Metadata))] class A { protected class Metadata { ... } } [MetadataType(typeof(B.Metadata))] class B { protected class Metadata { ... } } 定义,保留一个有助于将其父类描述为内部类的类非常有意义。如果您有可能想在其他地方重新使用内部课程,那么我会将它们移到父母之外。

我认为在实践中看到这一点有点不典型。我确信有很好的例子,但我敢打赌这种模式有更多不好的例子。

答案 2 :(得分:1)

当你的班级很小时看起来没问题。一旦它们变得臃肿,你真的开始考虑将它们移动到单独的文件中。

更重要的是,如果你想在相同的代码中同时使用A.B和M.B,你必须始终输入A.B和M.B,这可能很痛苦。

答案 3 :(得分:1)

我想说使用私有嵌套类有时候可行,但通常不是一个好的设计。我曾经在我的项目中重构了一个非常大的类来给它私有的嵌套类。我这样做的原因是有些方法需要几十个参数,这给了他们更合理的分组。从这个意义上讲,我认为嵌套类是一个很好的快速修复。这是有道理的,因为那个班级以外没有人对这些领域有任何用处。

通常,我会回避在初始设计中使用嵌套类 - 并且在重新设计之前考虑它们之前要三思而后行。在维护中,如果你有时间,最好重新设计整个类,并将它们分成单独的内部文件。

我认为这种策略对于可测试性比使用嵌套类更好。由于与外部类和应用程序中的其他类的更大依赖性,我的重构嵌套类比传递许多参数的原始大类更容易进行单元测试。如果您拆分嵌套类以使它们独立,您可以编写更多实际测试单元的离散单元测试,而不是有效地组合外部类和内部类的单元测试。这将使您更有信心说:“是的,内部类在单元测试级别工作”和“是的,外部类在单元测试级别工作”(它还测试它如何与内部类一起工作,例如在计算公式中)。

答案 4 :(得分:0)

我理解你的样本有点人为。但是,如果你的类名相似 - 或者相同 - 你真的不应该让它们成为嵌套类。作为一般规则,您应该回避使用嵌套类

如果我没记错,.NET Framework指南也建议使用嵌套类反对Nested Type Usage Guidelines有点旧(回到1.1版),但原则仍然适用。

  

如果满足以下条件,请不要使用嵌套类型:

     
      
  • 该类型必须由客户端代码实例化。如果类型有   公共构造函数,它可能不应该嵌套。理由   本指南背后的内容是,如果可以实例化嵌套类型,那么   表示该类型在库中有一个位置。您可以   创建它,使用它,并在不使用外部类型的情况下销毁它。   因此,它不应该嵌套。内在类型不应该广泛   在外部类型之外重复使用而与外部没有关系   类型。
  •   
  • 对类型的引用通常在客户端代码中声明。
  •   

答案 5 :(得分:0)

这实际上取决于嵌套类的功能。这与C ++ STL在每个类中以不同方式定义迭代器的方式非常相似。这种做法本身没有任何问题,只要每个概念的真实性不同,基于包含的类。

在某种程度上,这可能是风格和品味的问题,但就个人而言,只要它们真正不同并且依赖于封装类的定义,我个人就不会看到问题。但是,如果它们在课堂外公开可见,它往往会变得更加混乱。因此,我不会亲自公开这些课程。

答案 6 :(得分:0)

你可以使用命名空间来做这样的事情(只需在VS中创建一个新文件夹)。哪个更适合组织,并且几乎会给你相同的结果。

但是如果子类只与父类相关,那么我就不会看到它的危害。

然后再次,如果你正在调用它们同样的东西,我猜他们会做类似的下降,你可能想要研究抽象,也许你的父类也可以用不同的方式完成。真的取决于你需要他们做什么

答案 7 :(得分:0)

我喜欢这样做,对我而言,它使得使用更加清晰,特别是找到名字不是问题。但是我通常会尝试将其限制在私人课程或公共枚举上。 例如

class Text {
   enum Alignment

class UIElement {
   enum Alignment

class Quadtree {
   private class Node

class Octree {
   private class Node

答案 8 :(得分:0)

如果有任何机会(或商业原因)你必须在其他地方使用它,请不要创建嵌套类(使用命名空间而不是犹豫要创建具有长名称的类,如果你需要)。

例如,我在控制器和视图之间使用嵌套类进行DTO,或者在类中使用嵌套类来表示缓存条目。

答案 9 :(得分:0)

如果您想为它们命名相同但具有不同类型,则可以使用不同的命名空间。

Namespace1.MyClass{}
Namespace2.MyClass{}

尽管类名称相同,但最终会有两种不同的类型。

答案 10 :(得分:0)

嵌套类没有任何内在错误,只要您坚持以下经验法则:

  1. 绝不公开或内部。有一些特殊情况,例如当您使用嵌套类来实现IEnumerator时。但即使这样,类本身也应该保持私有,因为它的实例作为IEnumerator返回,并且它实际上只是作为避免使用不应该实例化的类来阻止命名空间的方法。

  2. 保持小。一个私有嵌套类,它实际上只是用于以更有条理的方式存储和传递数据,这很好,有时可能是一个非常有用的工具。 (并不完全不同于匿名类是如何有用的。)但是如果你想使用它们来打包大块的功能,它会变成代码味道,这表明你可能想要考虑重构外部类。