Как ограничить вложенные универсальные типы универсального метода
Я пытаюсь создать метод, который возвращает данные из базы данных на основе указанного универсального типа.
Интерфейс: (это определение компилируется)
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>
{
..
}