Подтвердить что ты не робот

С# Публичные вложенные классы или лучший вариант?

У меня есть схема управления, которая имеет несколько настроек и может иметь любое количество подключенных к ней датчиков (каждый с собственным набором настроек). Эти датчики могут использоваться только с цепью управления. Я думал об использовании вложенных классов следующим образом:

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. Было бы хорошо, если бы кто-то мог предложить короткий пример, в котором излагается структура, которую они будут использовать. Спасибо!

4b9b3361

Ответ 1

Содержимое класса должно быть деталями реализации этого класса. Являются ли детали реализации вложенных классов внешним классом или вы просто используете внешний класс в качестве удобного механизма определения имен и обнаружения?

Если первое, то вы не должны публиковать частные детали реализации. Сделайте их закрытыми, если они являются деталями реализации класса.

Если последнее, то вы должны использовать пространства имен, а не внешние классы, как механизм обзора и обнаружения.

В любом случае, публичные вложенные классы - плохой запах кода. Я хотел бы иметь очень вескую причину, чтобы выставить вложенный класс.

Ответ 2

У меня не слишком много проблем с общедоступными вложенными классами (я вообще не поклонник догматических правил), но вы считаете, что вместо этого ставите все эти типы в собственное пространство имен? Это более общий способ группировки классов.

EDIT: просто для того, чтобы уточнить, я бы очень редко использовал общедоступные вложенные классы, и я, вероятно, не использовал бы их здесь, но я бы не стал их полностью игнорировать. В структуре существует множество примеров открытых вложенных типов (например, List<T>.Enumerator) - без сомнения, в каждом случае дизайнеры рассматривали "запах" использования вложенного класса и считали его менее запахом, чем продвижение типа быть верхним уровнем или создавать новое пространство имен для задействованных типов.

Ответ 3

Из вашего комментария к Эрику:

Эти датчики могут ТОЛЬКО использоваться с определенной схемой

Этот вид отношений обычно известен как зависимость. Конструктор Sensor должен принимать ControlCircuit в качестве параметра. Вложенные классы не передают эту взаимосвязь.

и вы не можете получить/установить какие-либо настройки датчика, не пройдя через схему контроллера;

Я думаю, это означает, что все свойства Sensor будут делегировать (вызывать) или как-то сообщать (запускать событие) ControlCircuit, когда они будут использоваться. Или у вас будет какой-то внутренний интерфейс к датчику, который использует только схема управления, делая Sensor непрозрачный класс для внешнего мира. Если этот случай, Sensor является только деталью реализации и может быть вложенным частным или внутренним (также нет необходимости "сохранять" экземпляр датчика, если вы ничего не можете с ним поделать).

Кроме того, я даже не хочу раскрывать конструктор датчика (у контроллера будет метод для этого)

Тот факт, что конструктор Sensor теперь принимает схему управления, достаточно намека на то, что зависит от того, что вы могли оставить конструктором public. Вы также можете сделать это internal.

Общий комментарий, который у меня есть, заключается в том, что этот дизайн очень связан. Возможно, если бы у вас были некоторые интерфейсы между схемой управления, датчиком и настройками, было бы легче понять каждый компонент самостоятельно, и дизайн был бы более проверяемым. Мне всегда выгодно делать роли, которые каждый компонент играет в явном виде. То есть, если они не просто детали реализации.

Ответ 4

Я вообще не согласен с Эриком в этом вопросе.

Я обычно считаю: как часто конечный пользователь использует имя типа ControlCircuitLib.Sensor. Если это "почти никогда", но тип должен быть общедоступным, чтобы что-то было возможно ", затем перейдите к внутренним типам. Для чего-либо еще используйте отдельный тип.

Например,

public class Frobber {
    public readonly FrobType Standard = ...;
    public readonly FrobType Advanced = ...;

    public void Frob(FrobType type) { ... }

    public class FrobType { ... }
}

В этом примере FrobType действует только как непрозрачная "вещь". Только Frobber должен знать, что это на самом деле, хотя это должно быть возможным передать его вне этого класса. Однако такой пример довольно редок; чаще всего вам следует избегать вложенных публичных классов.

Одна из самых важных вещей при разработке библиотеки - сохранить ее просто. Поэтому используйте любой способ, который упрощает использование библиотеки и кода.

Ответ 5

Я бы сказал, что лучший вариант - это перемещение этих вложенных классов из класса, в котором они находятся, и заставить их стоять самостоятельно. Если я не пропущу что-то, вы обнаружите, что у вас есть только основной класс, чтобы использовать какую-то концепцию обзора, но на самом деле, для каких пространств имен.

Ответ 6

Мне нравятся вложенные классы в таких случаях, потому что они показывают взаимосвязь. Если вы не хотите, чтобы пользователи внешнего класса могли создавать элементы внутреннего класса отдельно от внешнего класса, вы всегда можете скрыть конструкторы и использовать методы factory во внешнем классе для создания элементов внутреннего класса. Я использую эту структуру много.

Ответ 7

Эта структура кажется мне вполне разумной. До сегодняшнего дня я не знал, что Microsoft не рекомендовала это делать, но я до сих пор не знаю, почему они посоветовали как таковой.

Я использовал эту структуру в ситуации, когда вложенный класс существует только для поддержки содержащего класса (т.е. части его реализации), но другие классы должны иметь возможность видеть его, чтобы взаимодействовать с содержащим классом (т.е. это часть API класса).

Говоря об этом, Эрик обычно знает, о чем он говорит, поэтому из уважения к его знаниям и на данный момент я перевел эти классы вместо использования пространств имен.

В настоящее время мне не нравятся результаты. У меня есть класс с именем BasicColumn, который существует только для представления столбца в классе Grid. Раньше этот класс всегда рассматривался как Grid.BasicColumn, и это было здорово. Это точно, как я хочу, чтобы на это ссылались. Теперь, когда Grid и BasicColumn находятся в пространстве имен Grids, он просто называется BasicColumn с использованием "Grids" в верхней части файла. Нет ничего, что указывало бы на его особые отношения с Grid, если я не хочу набирать все пространство имен (у которого есть несколько префиксов до того, как Grid я оставил для простоты).

Если кто-нибудь может указать на фактическую причину, по которой использование общедоступных классов является чем-то контрпродуктивным или субоптимальным, кроме того, что Microsoft не намерен использовать их таким образом, тогда я бы хотел его услышать.

Ответ 8

Пока я чувствую, что ответ Эрика верен, важно понять, что на самом деле он не полностью решает, какова ваша ситуация.

Ваше дело звучит очень похоже на то, что я часто нахожу себя там, где у вас есть класс, который действительно представляет собой реализацию другого класса, однако некоторые детали или функциональные возможности этого подкомпонента естественным образом поддаются прямому воздействию на некоторые незначительные аспекты, которые не регулируются родителем.

В этих случаях вы можете использовать интерфейсы. Вложенные классы не обязательно должны быть общедоступными, так как они действительно являются внутренними деталями класса, в который они вложены, но часть функциональности (интерфейса) должна быть общедоступной и может быть реализована классом.

Это позволяет строить внутренние структуры, которые будут контролироваться классом, в который они вложены, в то же время обеспечивая прямой доступ к типу из пространства имен для внешних ссылок. (Вызывающий будет использовать SomeNamespace.IChildApi как имя, а не SomeNamespace.NestingClass.NestedClass.SecondNestedClass.ThirdNestedClass и т.д.)