1

I have an m-to-n association between source and target formats. (right now, m=2 and n=5, with n growing faster than m).

I want to get an event, the input format being an Item or DbDataReader, and convert it into other types, which shall provide the required constructors:

public MyEvent(Item item)
public MyEvent(DbDataReader ddr)

public MyEventDetailed(Item item)
public MyEventDetailed(DbDataReader ddr)

public MyEventInfo(Item item)
public MyEventInfo(DbDataReader ddr)

public MyEventNotification(Item item)
public MyEventNotification(DbDataReader ddr)

public MyEventReminder(Item item)
public MyEventReminder(DbDataReader ddr)

Each constructors is to be used by exactly one of the two DataStores:

EwsDataStore : DataStoreBase
DbDataStore : DataStoreBase

which right now each implement the abstract getEvent method from the DataStoreBase:

abstract MyEventDetailed getEvent(string uniqueId);

Now I need all the other target formats as well, so I want to make them generic like this:

abstract T getEvent<T>(string uniqueId) where T : IEvent, new()

with a possible EwsDataStore implementation being

getEvent<T>(string uniqueId) where T : IEvent, new() // TODO: All affected models have to implement IEvent interface
{
    Item item;
    try {
        item = Item.Bind(service, new ItemId(uniqueId));
    } catch(Exception e) {
        throw new ArgumentException("An item by that Id was not found in the data store", "uniqueId", e);
    }
    // return T.FromItem(item); // Needs static IEvent.FromItem method.
    return new T(item); // IEvent has to enforce constructor with parameter Item on all implementing classes
}

and for Sql:

getEvent<T>(string uniqueId) where T:IEvent, new() // TODO: All affected models have to implement IEvent interface
{
    SQL db = new SQL();
    db.AddParameter("@uniqueId", uniqueId)
    SqlDataReader sdr = db.ExecuteReader("SELECT * FROM datastore WHERE uniqueId = @uniqueId");
    if(!sdr.HasRows) throw new ArgumentException("An item by that Id was not found in the data store");
    sdr.Read();
    // return T.FromDdr(sdr); // Needs static IEvent.FromDdr method.
    return new T(sdr); // IEvent has to enforce constructor with parameter DataReader on all implementing classes
}

But neither a constructor with parameters, nor a static method would be allowed on the generic type, each throwing one of the following two error messages:

The modifier 'static' is not valid for this item
'T': cannot provide arguments when creating an instance of a variable type

Searching for these error messages; I found that "the instance method can simply (sic!) delegate to a static method", or I can use Reflection or Activator.

None of them seems sufficiently easy/straightforward to write, understand, use and maintain.

Is there a straightforward way to use inheritance and generics to create all the different types based on the type I provide, which can be understood/followed by other programmers even if they are not proficient in-depth in C# inheritance?

Community
  • 1
  • 1
Alexander
  • 19,906
  • 19
  • 75
  • 162

1 Answers1

1

First off, I think you are misunderstanding the new constraint. new means that the generic type T has a parameterless constructor new T() which seems to be the opposite of what you want.

Simply remove the new constraint and create the instance of T using the Activator class: Activator.CreateInstance:

 return (T)Activator.CreateInstance(typeof(T), item);

Also, do note that you can not define static members in an interface nor can you inherit them. Therefore there is no way in the language to express a constraint on a generic type T that would allow you to call T.MyStaticMethod().

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • This would mean that a missing constructor causes a runtime error, not a compile-time error. Is there a possibility to make that a compile-time error? – Alexander Dec 27 '16 at 12:37
  • @Alexander No, there is no way to constaint `T` so that it has an accesible constructor, no matter what the signature is. `new` only enforces that `new T()` is a valid call. – InBetween Dec 27 '16 at 12:43