David Elentok
David Elentok

Reputation:

internal constructors in a C# library

I'm writing a data access library in C#, the structure of my data model classes are as follows (within the DataAccess assembly):

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) {...}
}
...

As you can see, each field has a different constructor.

To use this you automatically generate classes from an XML file, for example, a "Store" data model, which contains a "Customer" data class and an "Employee" data class will generate this code:

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 {...}

My problem with this code is that since the generated code is in a different assembly than the base data model classes, the StringField/IntegerField/... classes must have public constructors and that means anyone outside the data access assembly can create instances of them and I would like to prevent that.

Since the field classes have different constructors I can't think of a way to use reflection or generics to create the fields within the base data access assembly.

The only way I could think of was add a "Initialize" method to each field class that would include the properties of that specific fields and in the custom data class constructor do this:

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

This will allow me to change the field constructors to internal and won't allow anyone to create an instance of a field. The problem with this solution is that it would allow anyone to run the "Initialize" method and alter the field (this can be solved by adding a private boolean member (m_IsInitialized) that will be set to true when "Initialize" is first run, and each time "Initialize" is called, if m_IsInitialized is true, it will throw an exception, but this solution is a little ugly).

Does anyone have a better solution?

Upvotes: 1

Views: 1967

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500385

Well, three options:

  • Use InternalsVisibleTo to make the contructors visible
  • Write static methods in StringField etc to create instances
  • Write protected methods in DataModel or DataClass to create field instances (so CustomerDataClass could call DataClass.CreateStringField etc)

However, I don't know whether any of these is acceptable because it's not clear to me why you don't want the field classes to have public constructors anyway.

Upvotes: 3

Related Questions