2016-12-27 2 views
1

У меня есть связь m-to-n между исходными и целевыми форматами. (сейчас m = 2 и n = 5, причем n растет быстрее, чем m).Создавать различные типы данных с одинаковыми аргументами

Я хочу, чтобы получить событие, формат ввода Будучи Item или DbDataReader, и конвертировать его в другие виды, которые должны обеспечить необходимые конструкторы:

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) 

Каждые конструкторы должны быть использовано ровно один из двух DataStores:

EwsDataStore : DataStoreBase 
DbDataStore : DataStoreBase 

, который сейчас каждый реализовать абстрактный getEvent метод из DataStoreBase:

abstract MyEventDetailed getEvent(string uniqueId); 

Теперь мне нужно все другие целевые форматы, а также, так что я хочу, чтобы сделать их родовыми так:

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

с возможным осуществлением EwsDataStore быть

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 
} 

и для 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 
} 

Но ни конструктор с параметрами, ни статический Метод будет разрешен на универсальном типе, каждый бросает одну из следующих двух сообщений об ошибке:

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

Поиск этих сообщений об ошибках; Я обнаружил, что "the instance method can simply (sic!) delegate to a static method", или я могу использовать Reflection или Activator.

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

Есть ли простой способ использования наследования и генерических средств для создания всех разных типов на основе типа I, который может быть понят/за ним следуют другие программисты, даже если они не владеют глубиной в наследовании C#?

ответ

1

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

Просто удалите new ограничение и создать экземпляр T используя Activator класс: Activator.CreateInstance:

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

Также, обратите внимание, что вы не можете определить статические элементы в интерфейсе и не можете наследовать их. Поэтому на языке нет способа выразить ограничение на общий тип T, который позволит вам позвонить T.MyStaticMethod().

+0

Это означает, что отсутствующий конструктор вызывает ошибку времени выполнения, а не ошибку времени компиляции. Есть ли возможность сделать ошибку времени компиляции? – Alexander

+0

@Alexander Нет, невозможно ограничить 'T', чтобы он имел доступный конструктор, независимо от того, что такое подпись. 'new' только устанавливает, что' new T() 'является действительным вызовом. – InBetween