我有一个看起来像这样的函数:
static public IQueryable<TSource> OrderData<TSource, TKey>(this IQueryable<TSource> source,
System.Linq.Expressions.Expression<Func<TSource, TKey>> keySelector,
Sort.SortDirection sortDirection)
{
if (sortDirection == Sort.SortDirection.Ascending)
{
return source.OrderBy<TSource, TKey>(keySelector);
}
else
{
return source.OrderByDescending<TSource, TKey>(keySelector);
}
}
现在这很棒,直到我需要为IEnumerable容器做同样的事情为止。我可以这样称呼,在进出容器时进行转换,但是我想知道是否有一种方法可以使容器本身成为通用参数,并且仍然可以正常工作。
我想要类似的东西
static public C<TSource> OrderData<C, TSource, TKey>(this C<TSource> source,
System.Linq.Expressions.Expression<Func<TSource, TKey>> keySelector,
Sort.SortDirection sortDirection) where C : IEnumerable<TSource>
这不能编译,给出诸如“','期望”之类的基本错误。有什么想法吗?
答案 0 :(得分:3)
不,在C#中无法表达类型参数,该类型参数本身是某些任意类型,必须具有特定的泛型。 (这就是您尝试使用C
的样子。)
还值得注意的是,您可能始终不希望IEnumerable<T>
具有相同的签名,因为您通常使用委托而不是表达式树进行进程内查询。所以你有
public static IEnumerable<TSource> OrderData<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Sort.SortDirection sortDirection)
{
return sortDirection == Sort.SortDirection.Ascending
? source.OrderBy(keySelector)
: source.OrderByDescending(keySelector);
}
答案 1 :(得分:0)
Tl; dr:您总是可以在IQueryable<T>
上创建便宜的IEnumerable<T>
包装器,这非常好,并且可能会解决您的问题:
IEnumerable<int> nums_enumerable = new[] { 1, 2, 3 }.AsEnumerable();
IQueryable<int> nums_queryable = nums_enumerable.AsQueryable();
长版本是您的问题表明了为什么首先存在接口:告诉编译器,如果两个对象实现IEnumerable
,则这是一个契约,它们必须根据该契约来实现所有方法接口,对这些方法的调用在运行时不会失败。
您要问的是完全忽略接口,这被称为鸭子输入,实际上甚至在C#中使用反射或DLR(dynamic
关键字)中的achievable。但是,当您需要使用反射并且突然失去所有编译时检查和保证时,它确实散发出不良的设计气味:您的代码可能会在运行时工作,也许不会。在这种情况下,这将是一个非常糟糕的主意。
鸭式打字的例子通常是简单的用例,但要点是方法签名仍然必须 match 。在这种情况下,情况甚至更糟:此界面中的各种方法具有相同的名称,并且似乎以相同的方式工作,它们是完全不同的。
比较这两种情况,例如:
在IEnumerable<T>
情况下,我们称为public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
。该方法的参数是匿名方法,即在编译时生成的类中的方法,该方法接受参数并返回值乘以2。IEnumerable.Average
方法实际上为列表中的每个项目调用此方法:
double GetAverage(IEnumerable<int> items)
{
return items.Average(i => i * 2);
}
在IQueryable<T>
情况下,我们称为public static double Average<TSource>(this IQueryable<TSource> source, Expression<Func<TSource,int>> selector)
。此方法的参数是一个表达式树,即一个对象,其中包含我们想要将参数与2
相乘的信息。它从字面上包含一个类型为Multiply
的二进制表达式,该表达式引用了另外两个表达式(ParameterExpression
和ConstantExpression
)。 此对象本身传递给IQueryable.Average
时不执行任何计算:
double GetAverage(IQueryable<int> items)
{
return items.Average(i => i * 2);
}