Как ограничить вложенные универсальные типы универсального метода

Я пытаюсь создать метод, который возвращает данные из базы данных на основе указанного универсального типа.

Интерфейс: (это определение компилируется)

public interface IOrderPosition<TOrder, TArticle, TOrderPosition>
  where TOrder : IOrder
  where TArtile : IArticle
  where TOrderPosition : IOrderPosition<TOrder, TArticle, TOrderPosition>
{
  long? id { get; set; }
  TOrder order { get; set; }
  TArtile article { get; set; }
  List<TOrderPosition> subPositions { get; set; }
}

Возможная конкретная реализация: (это определение компилируется)

public class OrderPosition : IOrderPosition<Order, Article, OrderPosition>
{
  public long? id { get; set; }
  public Order order { get; set; }
  public Article article { get; set; }
  public List<OrderPosition> subPositions { get; set; }
}

Попытка написать универсальный метод на основе интерфейса: (это определение НЕ компилируется)

public List<TOrderPosition> GetOrderPositionOfOrder<TOrderPosition>(long? id) 
  where TOrder : IOrder
  where TArticle : IArticle
  where TOrderPosition : IOrderPosition<TOrder, TArticle, TOrderPosition>
{
  ..
}

Ошибки:

'DataSourceOrder.GetOrderPositionOfOrder<TOrderPosition>()' does not define type parameter 'TOrder'
'DataSourceOrder.GetOrderPositionOfOrder<TOrderPosition>()' does not define type parameter 'TArticle'
The type or namespace name 'TOrder' could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name 'TArticle' could not be found (are you missing a using directive or an assembly reference?)

Быть использованным так:

List<OrderPosition> positions = GetOrderPositionOfOrder<OrderPosition>(5);
List<TransferOrderPosition> transferPositions = GetOrderPositionOfOrder<TransferOrderPosition>(5);

Вопрос:

Почему это компилируется для интерфейса, но не для метода?

Я ожидал, что оба сработают или оба потерпят неудачу. Я предположил, что компиляция может вывести типы TOrder и TArticle из типа, заданного для TOrderPosition, который определяет конкретные типы как для статьи, так и для порядка.

Я хотел бы знать, почему это происходит, и если и как я могу решить проблему без необходимости явно указывать все типы.

3 ответа

Решение

Почему это компилируется для интерфейса, но не для метода?

Ну вы декларируете TOrder а также TArticle в IOrderPosition интерфейс, но не в GetOrderPositionOfOrder метод.

Вам необходимо объявить эти общие параметры в объявлении метода:

public List<TOrderPosition> GetOrderPositionOfOrder<TOrder, TArticle, TOrderPosition>(long? id)
    where TOrder : IOrder
    where TArticle : IArticle
    where TOrderPosition : IOrderPosition<TOrder, TArticle, TOrderPosition>
{
    ...
}

И назовите это так:

var list = GetOrderPositionOfOrder<Order, Article, OrderPosition>(5);

Но если вы хотите позвонить GetOrderPositionOfOrder лайк:

var list = GetOrderPositionOfOrder<OrderPosition>(5);

Ты можешь сделать IOrderPosition ковариантный в TOrder а также TArticle:

interface IOrderPosition<out TOrder, out TArticle, TOrderPosition>
    where TOrder : IOrder
    where TArticle : IArticle
    where TOrderPosition : IOrderPosition<TOrder, TArticle, TOrderPosition>
{
    long? id { get; set; }
    TOrder order { get; }
    TArticle Article { get; }
    List<TOrderPosition> subPositions { get; set; }
}

Обратите внимание, что Order а также Article должны быть только свойства получателя (но эти свойства в OrderPosition могу иметь set сбруя).

И метод:

public List<TOrderPosition> GetOrderPositionOfOrder<TOrderPosition>(long? id)
    where TOrderPosition : IOrderPosition<IOrder, IArticle, TOrderPosition>
{
    ...
}

Делая это, вы можете делать звонки, как GetOrderPositionOfOrder<OrderPosition>(5),

Посмотрите на ошибки:

"DataSourceOrder.GetOrderPositionOfOrder()" не определяет параметр типа "TOrder" "DataSourceOrder.GetOrderPositionOfOrder()" не определяет параметр типа "TArtile"

Вы имеете в виду параметры типа, которые не существуют.
Вы должны определить их в методе так же, как вы определяете их в интерфейсе:

public static List<TOrderPosition> GetOrderPositionOfOrder<TOrder, TArticle, TOrderPosition>(long? id)

Это означает, что вызов метода будет довольно уродливым:

var positions = GetOrderPositionOfOrder<Order, Position, OrderPosition>(5);
var transferPositions = GetOrderPositionOfOrder<TransferOrder, TransferArticle, TransferOrderPosition>(5);

Когда вы вызываете метод, вы должны предоставить все параметры типа или ни одного (если они могут быть выведены). Так оно и есть.

В интерфейсе вы определяете его как общий принимающий 3 типа TOrder, TArticle, TOrderPositionТаким образом, вы можете ограничить эти типы.

Ваш метод определяет только один тип, TOrderPositionи компилятор не может вывести тот факт, что вам нужны другие типы из ограничения where TOrderPosition : IOrderPosition<TOrder, TArticle, TOrderPosition> в вашем определении метода.

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

public List<TOrderPosition> GetOrderPositionOfOrder<TOrder, TArticle, TOrderPosition>(long? id) 
 where TOrder : IOrder
 where TArticle : IArticle
 where TOrderPosition : IOrderPosition<TOrder, TArticle, TOrderPosition>
{
 ..
}
Другие вопросы по тегам