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

3
我将尝试创建一个方法,它基于给定的泛型类型从数据库返回数据。 接口:(此定义可以编译)
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>
{
  ..
}

Errors:

'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
但是你的方法中没有定义 TOrder 和 TArticle 的泛型参数,否则它将可以编译通过。对于类,你指定了所有三个(TOrder、TArticle 和 TOrderPosition)。 - Evk
看了这些答案,很明显这是行不通的。我只是希望编译器能够从 OrderPosition 的类型中推断出缺失的类型,因为我想避免列出所有类型(实际上有多于那三个)。 - @Evk - Holly
3个回答

5
为什么接口可以编译,但方法却不能?
因为你在 IOrderPosition 接口中声明了 TOrder 和 TArticle,但在 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必须是只读属性(但是OrderPosition中的这些属性可以具有set访问器)。
而且方法为:
public List<TOrderPosition> GetOrderPositionOfOrder<TOrderPosition>(long? id)
    where TOrderPosition : IOrderPosition<IOrder, IArticle, TOrderPosition>
{
    ...
}

这样做可以像这样调用函数:GetOrderPositionOfOrder<OrderPosition>(5)。该函数与订单位置相关,适用于IT技术领域。

感谢您展示了一种不需要显式声明所有类型的方法,因为这会削弱整个解决方案的优雅性。不幸的是,这对我可能没有帮助,因为我需要它用于 .Net 3.5 CF,我还需要设置那些方法不读取的值。 - Holly
@Holly:嗯,那么也许你可以创建一个基础接口,例如 IOrderPosition<TOrderPosition> where TOrderPosition: IOrderPosition<TOrderPosition>(如果需要,可以包含 idsubPositions 属性),然后你的当前接口继承自这个基础接口。然后你可以声明这样的方法:GetOrderPositionOfOrder<TOrderPosition>(long? id) where TOrderPosition : IOrderPosition<TOrderPosition> - Arturo Menchaca
1
谢谢您的建议,我一直在考虑同样的事情,但我不确定这是否值得一试,因为我需要反映所有类型和转换等。我现在知道嵌套泛型类型不会被推断(我原以为会),我甚至尝试提供OrderPosition的具体类型实例作为参数,希望推断出所有类型参数,但也没有成功 - 现在我有点想念Java的“?extends ClassName”;-) - Holly

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

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


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>
{
 ..
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接