Как получить аргумент универсального базового класса во время компиляции? (т.е. без использования Reflection) - PullRequest
4 голосов
/ 14 июня 2011

Я хочу реализовать универсальный метод для извлечения данных заголовка / подробностей из базы данных:

public static T RetrieveHeaderDetail<T>
    where T : Header<???>, new()
    // Where ??? means "what can I do here?"
{
    // ...
}

Вот определение универсального элемента, представляющего заголовок документа:

public class Header<TDetail> where TDetail : class, new()
{
    public List<TDetail> Details;
}

А вот несколько примеров:

public class RequestForQuotation : Header<RequestForQuotationDetail> { ... }
public class Order               : Header<OrderDetail>               { ... }
public class Invoice             : Header<InvoiceDetail>             { ... }
// ..

Нетрудно доказать это, поскольку .NET не допускает ни множественного наследования, ни "общей специализации" (которая позволила бы Header<U> наследоваться от некоторого другого Header<V>), для любого конкретного T, существует не более одного U, такого, что T наследует (прямо или косвенно) от Header<U>. Более того, тривиально найти тип U: перебирать базовые типы T до тех пор, пока вы не найдете экземпляр Header<U>, а затем просто принять аргумент универсального шаблона! Тем не менее, C # хочет, чтобы я изменил определение моего метода на следующее:

public static T RetrieveHeaderDetail<T,U>
    where T : Header<U>, new()
    where U : class,     new()
{
    // ...
}

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


Когда я сталкиваюсь с такими проблемами, я действительно, действительно скучаю по C ++.

Ответы [ 2 ]

1 голос
/ 14 июня 2011

Я задал этот вопрос не так давно.

Обобщения с общими параметрами и классом Abstract

0 голосов
/ 14 июня 2011

Я не уверен, что полностью понимаю, что вам нужно, но не могли бы вы определить интерфейс и использовать его для указания ограничения?

Например, у нас есть что-то вроде этого в нескольких местах:

public class Reader<T> where T : IInt32Id
{
   public T GetById(int Id)
   {
      // get by id
   }
}

Затем я просто использую IInt32Id в качестве интерфейса для получения всех моих классов, которые имеют поле int (в отличие от long) ID.

...