Внутренние конструкторы в библиотеке C#

Я пишу библиотеку доступа к данным на C#, структура моих классов модели данных следующая (в сборке DataAccess):

public abstract class DataModel {...} (protected constructor)
public abstract class DataClass {...} (protected constructor)
public abstract class DataField {...} (protected constructor)
public class IntegerField : DataField
{
   public IntegerField(DataClass cls, string name, bool isNullable, 
                       int? defaultValue, int? minValue, int? maxValue) {...}
}
public class StringField : DataField
{
   public StringField(DataClass cls, string name, bool isNullable, 
                      string defaultValue, int maxLength) {...}
}
public class BooleanField : DataField
{
   public BooleanField(DataClass cls, string name, bool isNullable, 
                       bool? defaultValue) {...}
}
...

Как видите, у каждого поля свой конструктор.

Чтобы использовать это, вы автоматически генерируете классы из файла XML, например, модель данных «Магазин», которая содержит класс данных «Клиент» и класс данных «Сотрудник», сгенерирует этот код:

public class StoreDataModel : DataModel
{
   public CustomerDataClass Customer { get; private set;}
   public EmployeeDataClass Employee { get; private set;}
   public StoreDataModel()
   {
     Customer = new CustomerDataClass(this);
     AddClass(customer)
     Employee = new EmployeeDataClass(this);
     AddClass(employee)
   }
}
public class CustomerDataClass : DataClass
{
   public StringField FirstNameField { get; private set; }
   public StringField LastNameField { get; private set; }
   public DateField BirthDateField { get; private set; }

   public CustomerDataClass(StoreDataModel model) : base(model)
   {
      FirstNameField = new StringField(this, "FirstName", true, null, 50);
      LastNameField = new StringField(this, "LastName", true, null, 50);
      ...
   }
}
public class EmployeeDataClass : DataClass {...}

Моя проблема с этим кодом заключается в том, что, поскольку сгенерированный код находится в другой сборке, чем классы базовой модели данных, классы StringField / IntegerField / ... должны иметь общедоступные конструкторы, а это означает, что любой, кто находится за пределами сборки доступа к данным, может создавать их экземпляры. и я хотел бы предотвратить это.

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

Единственный способ, который я мог придумать, - это добавить метод «Initialize» к каждому классу поля, который будет включать свойства этих конкретных полей, и в конструкторе пользовательского класса данных сделайте следующее:

   public CustomerDataClass(StoreDataModel model) : base(model)
   {
      FirstNameField = AddField<StringField>(this, "FirstName", true, null)
                              .Initialize(50);
      LastNameField = AddField<StringField>(this, "LastName", true, null)
                              .Initialize(50);
      ...
   }

Это позволит мне изменить конструкторы полей на внутренние и никому не позволит создать экземпляр поля. Проблема с этим решением заключается в том, что оно позволяет любому запускать метод «Initialize» и изменять поле (это можно решить, добавив частный логический член (m_IsInitialized), которому будет присвоено значение true при первом запуске «Initialize», и каждый раз, когда вызывается "Initialize", если m_IsInitialized истинно, он генерирует исключение, но это решение немного уродливо).

Есть ли у кого-нибудь лучшее решение?

Не говоря уже о том, что если вы выполняете инициализацию вне конструктора, вы теряете гарантию того, что инварианты вашего класса сохраняются, поскольку вы полагаетесь на вызывающую сторону, чтобы «помочь» с построением объекта. Но я не программист на C#, поэтому ничем не могу помочь. :)

Adam Bellaire 09.01.2009 15:56
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
1 761
1

Ответы 1

Ну и три варианта:

  • Используйте InternalsVisibleTo, чтобы сделать конструкторы видимыми
  • Напишите статические методы в StringField и т. д. Для создания экземпляров
  • Запишите защищенные методы в DataModel или DataClass для создания экземпляров полей (чтобы CustomerDataClass мог вызывать DataClass.CreateStringField и т. д.)

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

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

David Elentok 09.01.2009 16:07

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

David Elentok 09.01.2009 16:08

«InternalsVisibleTo» мне не помогает, потому что сборка доступа к данным является общей библиотекой, которая будет использоваться с различными проектами. Чтобы использовать его, мне пришлось бы изменить сборку доступа к данным для каждого нового проекта (добавить еще один атрибут InternalsVisibleTo)

David Elentok 09.01.2009 19:52

Вы до сих пор не объяснили, почему не хотите, чтобы другие сборки создавали экземпляры.

Jon Skeet 09.01.2009 20:13

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

David Elentok 10.01.2009 17:52

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

Jon Skeet 10.01.2009 18:24

Защищенный метод в DataModel / DataClass, конечно, затруднит выполнение случайно.

Jon Skeet 10.01.2009 18:25

Другие вопросы по теме