如何约束泛型方法的嵌套泛型类型

How to constrain nested generic types of a generic method

我正在尝试创建一个方法,该方法 returns 基于给定泛型的数据库中的数据。

接口:(本定义编译)

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);

问题:

为什么这个编译是针对接口,而不是针对方法?

我希望两者都能工作或都失败。我假设编译可以从为 TOrderPosition 给出的类型推断出 TOrder 和 TArticle 的类型,TOrderPosition 定义了文章和订单的具体类型。

我想知道为什么会发生这种情况,以及是否可以以及如何在不必明确指定所有类型的情况下解决问题。

在接口中,您将其定义为接受 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>
{
 ..
}

看看错误:

'DataSourceOrder.GetOrderPositionOfOrder()' does not define type parameter 'TOrder' 'DataSourceOrder.GetOrderPositionOfOrder()' does not define type parameter '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);

调用该方法时,必须提供所有类型参数,或none(如果可以推断)。就是这样。

Why does this compile for the interface, but not for the method?

好吧,您在 IOrderPosition 接口中声明了 TOrderTArticle,但没有在 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);

您可以使 IOrderPositionTOrderTArticle 中协变:

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; }
}

注意 OrderArticle 必须是 getter-only 属性(但是 OrderPosition 中的这些属性可以有 set 访问器)。

以及方法:

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

这样你就可以像GetOrderPositionOfOrder<OrderPosition>(5)那样进行调用了。