【问题标题】:c# Public Nested Classes or Better Option?c#公共嵌套类还是更好的选择?
【发布时间】:2011-11-02 17:16:07
【问题描述】:

我有一个具有多种设置的控制电路,并且可能连接了任意数量的传感器(每个传感器都有自己的一组设置)。这些传感器只能与控制电路一起使用。我想过像这样使用嵌套类:

public class ControlCircuitLib
{
    // Fields.
    private Settings controllerSettings;
    private List<Sensor> attachedSensors;

    // Properties.
    public Settings ControllerSettings
    { get { return this.controllerSettings; } }

    public List<Sensor> AttachedSensors
    { get { return this.attachedSensors; } }

    // Constructors, methods, etc.
    ...

    // Nested classes.
    public class Settings
    {
       // Fields.
       private ControlCircuitLib controllerCircuit;
       private SerialPort controllerSerialPort;
       private int activeOutputs;
       ... (many, many more settings)

       // Properties.
       public int ActiveOutputs
       { get { return this.activeOutputs; } }
       ... (the other Get properties for the settings)

       // Methods.
       ... (method to set the circuit properties though serial port)        
    }

    public class Sensor
    {
       // Enumerations.
       public enum MeasurementTypes { Displacement, Velocity, Acceleration };

       // Fields.
       private ControlCircuitLib controllerCircuit;
       private string sensorName;
       private MeasurementTypes measurementType;
       private double requiredInputVoltage;
       ... (many, many more settings)

       // Properties.
       public string SensorName {...}
       ... (Get properties)

       // Methods.
       ... (methods to set the sensor settings while attached to the control circuit)
    }
}

我读过公共嵌套类是“禁止”的,但也有例外。这种结构可以吗?还是有更好的选择?

谢谢!

编辑

下面是我尝试为其编写库类的控制电路的粗略层次结构;我使用代码格式化来防止文本换行。

Control Circuit (com. via serial port) -> Attached Sensors (up to 10) -> Sensor Settings (approx. 10 settings per sensor)
                                          Basic Controller Settings (approx. 20 settings)
                                          Output Settings (approx. 30 settings)
                                          Common Settings (approx. 30 settings)
                                          Environment Settings (approx. 10 settings)

所有设置都是通过控制器设置的,但我想要一个有组织的库,而不是仅仅将所有 ~100 种方法、属性和设置都塞在一个 Controller 类下。如果有人能提供一个简短的例子来概述他们将使用的结构,那将不胜感激。谢谢!

【问题讨论】:

  • 如何“配置”控制电路?您是否根据设置在内部创建传感器?这些设置是否定义了传感器的配置?
  • @Jordão:基本上,控制电路具有内置的串行功能,用于设置电路(和连接的传感器)的设置。我想创建一个库类,这样我就不必手动创建命令字符串(例如SetSensorSettingA(sensorInstance, -20) 而不是发送“NA,BL,01,-20,\r”)。我是机甲。英。所以我只是想知道真正的程序员会如何组织这个课程:)
  • 有完整的源代码示例工作的最终解决方案吗?

标签: c# nested-class


【解决方案1】:

类的内容应该是那个实现细节。是外部类的嵌套类实现细节,还是您只是将外部类用作方便的名称范围和发现机制

如果是前者,那么您不应该公开提供私有实现细节。如果它们是类的实现细节,请将它们设为私有。

如果是后者,那么您应该使用命名空间,而不是外部类,作为您的作用域和发现机制。

无论哪种方式,公共嵌套类都是一种不好的代码气味。我希望有充分的理由公开嵌套类。

【讨论】:

  • 一个类的公共方法也是一个类的 API 的一部分 - 它们不应该仅仅被视为实现细节,特别是如果它们不是在(比如说)一个接口中指定。只在外部类的 API 中使用的类型被声明为嵌套类型是不是很糟糕?这不是我经常做的事情,但我认为在 2.0 之前的日子里,only 用于声明的事件的委托类型是有意义的比如那个类型。
  • (如果有人有任何疑问,我想我最终会像往常一样同意 Eric 的观点——但我只是想更详细地探讨一下这些想法。)跨度>
  • @Jon:我不清楚嵌套的 type 表示命令和控制层次结构。以反射为例。假设只有通过 MethodInfo 才能获得 ParameterInfo,除非通过 Type,否则无法获得 MethodInfo,除非通过 Assembly,否则无法获得 Type。要不要有四级类型嵌套,其中Type是Assembly的嵌套类型,MethodInfo是Type的嵌套类型,等等?
  • @DCShannon:命名空间特性的目的组织类型,嵌套类型的目的特性是允许使用类型作为其他类型的私有实现细节。因此,该建议的原因是更普遍的好建议的结果:不要使用功能来违背其预期目的
  • @DCShannon:此外,如果不是微软,您希望哪个实体为微软为微软客户正确、预期和有效地使用微软创建的工具制定指导方针? “因为 Microsoft 这么说”似乎是遵循 Microsoft 产品使用指南的一个很好的理由。
【解决方案2】:

我对公共嵌套类没有太多问题(我一般不喜欢教条规则),但您是否考虑过将所有这些类型放在它们自己的命名空间中?这是将类分组在一起的更常见方式。

编辑:澄清一下,我很少使用公共嵌套类,我可能不会在这里使用它们,但我也不会完全拒绝它们。框架中有很多公共嵌套类型的例子(例如List&lt;T&gt;.Enumerator)——毫无疑问,在每种情况下,设计者都认为使用嵌套类的“味道”,并认为它比推广类型更难闻成为顶级的,或者为所涉及的类型创建一个新的命名空间。

【讨论】:

  • 围绕所有类的命名空间是个好主意,但不能只将外部类设置为命名空间,因为它包含数据。
  • @drdwilcox:不,我不是在暗示。我的意思是显示的所有类型,即包括ControlCircuitLib(可能应该重命名)。
  • 我意识到了这一点。我只是添加了一些特殊性,主要是因为外部类的名称。
【解决方案3】:

从您的评论到 Eric 的回答:

这些传感器只能用于特定电路

这种关系通常称为依赖Sensor 构造函数应该将ControlCircuit 作为参数。嵌套类传达这种关系。

如果不通过控制器电路,您将无法获取/设置任何传感器设置;

我认为这意味着所有Sensor 属性在使用时都会委托给(调用)或以某种方式通知(触发事件)ControlCircuit。或者,您将拥有某种只有控制电路使用的传感器内部接口,使Sensor 对外部世界成为一个不透明 类。如果是这种情况,Sensor 只是一个实现细节,可以嵌套私有或内部(如果您无能为力,也无需“保存”传感器实例)。

另外,我什至不想公开 Sensor 构造函数(控制器将为此提供方法)

Sensor 构造函数现在采用控制电路这一事实足以暗示什么取决于您可以离开构造函数public。你也可以internal

我的一般评论是这种设计非常耦合。也许如果你在控制电路、传感器和设置之间有一些接口,独立理解每个组件会更容易,设计也会更可测试。我总是发现使每个组件扮演的角色显式是有益的。也就是说,如果它们不仅仅是实现细节

【讨论】:

  • 是的,我的 Sensor 类实际上确实将 ControlCircuit 作为参数。此外,我通过串行端口与控制电路通信,并使用它来获取/设置各种控制器(和连接的传感器)设置;我需要有一个公开可用的类来“存储”附加的传感器设置。例如:Sensor sensor1Settings = controllerInstance.AttachedSensors[0];。我已经编辑了我的原始帖子以包含电路的层次结构,也许这将有助于说明我想要完成的事情。我是机甲。英。所以我想像一个真正的程序员那样写这个类:)
  • 那么看起来你的Sensor 真的应该公开。但是您不需要将它嵌套来传达它取决于 取决于ControlCircuit
【解决方案4】:

我会说更好的选择是将这些嵌套类移出它们所在的类并让它们独立存在。除非我遗漏了某些东西,否则您似乎只是为了某种范围概念而将它们放在主类中,但实际上,这就是名称空间的用途。

【讨论】:

    【解决方案5】:

    在这点上我通常不同意 Eric。

    我通常考虑的事情是:最终用户应该多久使用一次类型名称ControlCircuitLib.Sensor。如果它“几乎从不,但类型需要是公共的,以便可以做某事”,那么选择内部类型。对于其他任何事情,请使用单独的类型。

    例如,

    public class Frobber {
        public readonly FrobType Standard = ...;
        public readonly FrobType Advanced = ...;
    
        public void Frob(FrobType type) { ... }
    
        public class FrobType { ... }
    }
    

    在此示例中,FrobType 仅充当不透明的“事物”。只有Frobber 需要知道它实际上是什么,尽管它需要能够在该类之外传递它。但是,这种例子很少见。通常情况下,您应该避免使用嵌套的公共类。

    设计库时最重要的事情之一就是保持简单。因此,无论哪种方式都可以使库和使用代码更简单。

    【讨论】:

    • 如果FrobType 看起来像应用程序更感兴趣的是FrobType 具有某些成员而不是知道它是什么,那么您的示例可能会更清楚。例如,如果一个方法返回几个值,返回一个包含这些值的结构可能比拥有多个ref 参数更有效。代码可以说var result=myThing.ComputeStuff(); if (result.Minvalue) ... if (result.MaxValue)... 等。如果Result 是一个结构,添加成员将需要重新编译代码,但如果唯一使用结构类型的代码是......
    • ...调用myThing.ComputeStuff() 的代码可能还不错。如果其他 unrelated 代码也使用该结构,情况会更糟。将结果类型设置为 myThing 类型中的嵌套类型将有助于阻止此类使用;将其公开(例如 KeyValuePair&lt;TKey,TValue&gt;,主要存在于 Dictionary&lt;TKey,TValue&gt;,鼓励它。
    【解决方案6】:

    我喜欢这种情况下的嵌套类,因为它显示了关系。如果您不希望外部类的用户能够独立于外部类创建内部类的项目,您始终可以隐藏构造函数并使用外部类中的工厂方法来创建内部类的元素。我经常使用这种结构。

    【讨论】:

      【解决方案7】:

      这种结构在我看来完全合理。直到今天我才知道微软已经建议不要这样做,但我仍然不知道他们为什么会这样建议。

      我在嵌套类仅存在以支持包含类(即它是其实现的一部分)的情况下使用此结构,但其他类需要能够看到它才能与包含类交互(即它是类 API 的一部分)。

      话虽如此,Eric 通常知道他在说什么,所以出于对他知识的尊重,我暂时将这些类转换为使用命名空间。

      目前,我不喜欢结果。我有一个名为 BasicColumn 的类,它的存在仅用于表示名为 Grid 的类中的一列。以前,该类总是被称为 Grid.BasicColumn,这很棒。这正是我希望它被提及的方式。现在,Grid 和 BasicColumn 都在 Grids 命名空间中,它被称为 BasicColumn,文件顶部有一个“使用 Grids”。没有什么可以表明它与 Grid 的特殊关系,除非我想输入整个命名空间(为简单起见,我在 Grid 之前省略了几个前缀)。

      如果有人能指出使用公共嵌套类在某种程度上会适得其反或次优的实际原因,除了微软不打算以这种方式使用它们这一无关紧要的事实,那么我很想听听。

      【讨论】:

      • 我要补充一点,我最终使用公共嵌套类的原因与您完全相同,我认为这实际上并不违反 Eric 所描述的内容。嵌套类的要点是它们是它们嵌套的类的范围实现细节,它们仅公开以公开所需的 API。理想情况下,您只需将嵌套类设置为受保护或私有,然后让它实现一个提供 API 的公共接口。
      • @AJHenderson 这听起来像是一个计划。
      【解决方案8】:

      虽然我觉得 Eric 的回答是正确的,但重要的是要意识到它并没有真正完全解决您的情况。

      您的案例听起来与我经常发现自己的案例非常相似,您的类实际上是另一个类的实现细节,但是,该子组件的某些细节或功能自然会使其直接暴露给一些次要的不受父级管理的方面。

      在这些情况下,您可以做的是使用接口。嵌套类不必公开,因为它们实际上是它们嵌套在其中的类的内部细节,但需要公开功能的子集(接口)并可由该类实现。

      这允许内部结构的构造由它们嵌套的类控制,同时仍然允许从命名空间直接访问类型以供外部引用。 (调用者将使用 SomeNamespace.IChildApi 作为名称,而不是 SomeNamespace.NestingClass.NestedClass.SecondNestedClass.ThirdNestedClass 等)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-30
        • 2010-09-25
        • 2017-11-01
        • 1970-01-01
        • 2019-04-02
        • 2019-04-16
        • 1970-01-01
        相关资源
        最近更新 更多