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

时间:2016-11-24 15:29:11

标签: c# generics nested-generics generic-constraints

我正在尝试创建一个方法,该方法根据给定的泛型类型从数据库返回数据。

界面:(此定义编译)

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的类型,它定义了文章和订单的具体类型。

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

3 个答案:

答案 0 :(得分:3)

  

为什么这会为接口编译,而不是为方法编译?

嗯,您在TOrder界面中声明了TArticleIOrderPosition,但未在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);

您可以在IOrderPositionTOrder中提出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; }
}

请注意,OrderArticle必须是仅限getter的属性(但OrderPosition中的这些属性可以包含set个访问者。

方法:

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

这样做可以进行GetOrderPositionOfOrder<OrderPosition>(5)之类的通话。

答案 1 :(得分:2)

看一下错误:

  

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

调用方法时,必须提供所有类型参数,或者不提供任何类型参数(如果可以推断它们)。这就是它的方式。

答案 2 :(得分:0)

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