Я пишу библиотеку доступа к данным на 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 истинно, он генерирует исключение, но это решение немного уродливо).
Есть ли у кого-нибудь лучшее решение?





Ну и три варианта:
Однако я не знаю, приемлемо ли что-либо из этого, потому что мне непонятно, почему вы все равно не хотите, чтобы классы полей имели общедоступные конструкторы.
Во-первых, причина, по которой я не хочу помещать общедоступный конструктор в классы полей, заключается в том, что никто не будет пытаться создать новое поле и использовать его где-нибудь в остальной части кода (маловероятно, но все же ...) - InternalsVisibleTo заставляет сборка доступа к данным, чтобы узнать сборку пользовательской модели данных. ...
- статические методы позволят любому создавать поля - я думал об этом, но всякий раз, когда я добавляю другой класс поля, мне нужно будет отредактировать класс DataClass ... это не самый чистый способ.
«InternalsVisibleTo» мне не помогает, потому что сборка доступа к данным является общей библиотекой, которая будет использоваться с различными проектами. Чтобы использовать его, мне пришлось бы изменить сборку доступа к данным для каждого нового проекта (добавить еще один атрибут InternalsVisibleTo)
Вы до сих пор не объяснили, почему не хотите, чтобы другие сборки создавали экземпляры.
О, я не хочу, чтобы какой-либо пользователь создавал экземпляр класса поля, а затем пытался использовать его с остальной частью кода (класс поля пользователя может быть несуществующим полем и испортить систему). Я хочу быть уверен, что никто, кроме меня, не может создавать поля
Что ж, если код, определяющий, сколько полей необходимо / создано, не находится под вашим контролем, мне это кажется принципиально невозможным.
Защищенный метод в DataModel / DataClass, конечно, затруднит выполнение случайно.
Не говоря уже о том, что если вы выполняете инициализацию вне конструктора, вы теряете гарантию того, что инварианты вашего класса сохраняются, поскольку вы полагаетесь на вызывающую сторону, чтобы «помочь» с построением объекта. Но я не программист на C#, поэтому ничем не могу помочь. :)