c#公共嵌套类或更好的选项?

我有一个控制电路,它有多种设置,可以连接任意数量的传感器(每个传感器都有自己的设置)。 这些传感器只能与控制电路一起使用。 我想过使用嵌套类,如下所示:

public class ControlCircuitLib { // Fields. private Settings controllerSettings; private List attachedSensors; // Properties. public Settings ControllerSettings { get { return this.controllerSettings; } } public List 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) 

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

类的内容应该是该类的实现细节 。 嵌套类是外部类的实现细节 ,还是仅仅使用外部类作为方便的名称范围和发现机制

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

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

无论哪种方式,公共嵌套类都是一个糟糕的代码味道。 我想有一个很好的理由来公开一个嵌套类。

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

编辑:只是为了澄清,我很少使用公共嵌套类,我可能不会在这里使用它们,但我也不会完全不喜欢它们。 框架中有很多公共嵌套类型的例子(例如List.Enumerator ) – 毫无疑问,在每种情况下,设计师都会考虑使用嵌套类的“气味”,并认为它不是一种气味而是将类型提升为顶级类型,或为所涉及的类型创建新的命名空间。

从你的评论到Eric的回答:

这些传感器只能与特定电路一起使用

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

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

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

另外,我甚至不想暴露传感器构造函数(控制器将有一个方法)

Sensor构造函数现在采用控制电路的事实足以说明取决于您可以将构造函数public 。 你也可以把它做成internal

我的一般评论是这种设计是非常耦合的 。 也许如果你在控制电路,传感器和设置之间有一些接口,那么独立地理解每个组件会更容易,并且设计将更加可测试。 我总是觉得有利于使每个组件发挥作用的角色 。 也就是说,如果它们不仅仅是实施细节

我一般不同意埃里克的观点。

我通常会考虑的事情是:最终用户应该多久使用一个类型名称ControlCircuitLib.Sensor 。 如果它“几乎从不,但类型需要公开,以便做某事可能”,那么去内部类型。 除此之外,请使用单独的类型。

例如,

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

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

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

我会说更好的选择是将这些嵌套类移出他们所在的类并让它们独立存在。 除非我遗漏了某些东西,否则你只能在主类中使用它们以获得某种范围概念,但实际上,这就是命名空间的用途。

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

这个结构对我来说似乎完全合理。 直到今天我都没有意识到微软已经建议不要这样做,但我仍然不知道为什么他们这样做了。

我已经在嵌套类只存在以支持包含类(即它是其实现的一部分)的情况下使用了这个结构,但是其他类需要能够看到它以便与包含类进行交互(即它是该类API的一部分)。

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

目前,我不喜欢结果。 我有一个名为BasicColumn的类,它只存在于一个名为Grid的类中的列。 以前,该类始终被称为Grid.BasicColumn,这很棒。 这正是我希望它被引用的方式。 现在,Grid和BasicColumn都在Grids命名空间中,它只是被称为BasicColumn,在文件的顶部有一个’using Grids’。 没有什么可以表明它与Grid的特殊关系,除非我想输出整个命名空间(在Grid之前有一些前缀我为了简单而省略了)。

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

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

你的案例听起来非常类似于我经常发现你所拥有的一个类,它实际上是另一个类的实现细节,但是,该子组件的一些细节或function自然会直接暴露于某些次要方面。不受父母的管辖。

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

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