我想在基类中使用一些System.Linq
扩展方法,而派生类应该能够通过覆盖特定方法来提供一些表达式。
当前基类代码:
lQuery.OrderBy(s => s.ID) <-- works, *1
我希望通过可以覆盖的方法调用替换s => s.ID
:
... Expression<Func<TSource, TKey>> GetOrderByKey<TSource, TKey>()
{
...
}
但是当我覆盖像
这样的方法时override Expression<Func<TSource, TKey>> GetOrderByKey<TSource, TKey>()
{
return s => s.ID; <-- compiler error, *2
}
编译器输出错误
无法将类型'int'隐式转换为'TKey'
为什么通用参数TKey
在第1行中神奇地推导出(和什么?),但在第2行中,它被推断为ID属性的实际类型?
如何解决编译错误?
提前致谢。
修改
我很难找到问题的正确解释。我们将其分解为以下简单的行:
var lQuery = from s in ... select s;
lQuery = lQuery.OrderBy(s => s.ID); // *3
编译器解释OrderBy
调用究竟是如何产生ORDER BY ID ASC
而不是ORDER BY %Value of ID% ASC
?编译器似乎以某种方式决定推导s.ID
到property name "ID"
,而不是采用实际的属性数据类型,从而得到int值。
EDIT2
好的,+ D Stanley的另一个例子
这有效:
void SetOrder<TKey>(IQueryable<%type of s%> aQuery, Expression<Func<%type of s%, TKey>> aKeySelector)
{
aQuery.OrderBy(aKeySelector);
}
...
SetOrder(aQuery, s => s.ID); // <-- works
但这不是(编译错误显示如前所述)
protected void SetOrder<TKey>(IQueryable<%type of s%> aQuery)
{
Expression<Func<%type of s%, TKey>> lKeySelector = s => s.ID; // <-- deduced to int
aQuery.OrderBy(lKeySelector);
}
答案 0 :(得分:1)
OrderBy
如何推断TSource
和TKey
的类型
编译器可以通过查看:
来推断TSource
和TKey
的类型
lQuery
的类型 - 在本例中为IQueryable<{your class}>
ID
{your class}
属性的类型
醇>
如何解决编译错误?
您要求调用者指定TKey
的类型(通过通用参数),但在您的方法中,您指定的是已知的表达式输入的。您可以删除泛型参数:
protected void SetOrder(IQueryable<%type of s%> aQuery)
{
Expression<Func<%type of s%, int>> lKeySelector = s => s.ID;
aQuery.OrderBy(lKeySelector);
}
但请注意,OrderBy
会返回一个查询 - 它不会更改原始查询,所以真正想要的是类似的东西:
protected IQueryable<%type of s%> SetOrder(IQueryable<%type of s%> aQuery)
{
Expression<Func<%type of s%, int>> lKeySelector = s => s.ID;
return aQuery.OrderBy(lKeySelector);
}
或可能
protected void SetOrder(ref IQueryable<%type of s%> aQuery)
{
Expression<Func<%type of s%, int>> lKeySelector = s => s.ID;
aQuery = aQuery.OrderBy(lKeySelector);
}
但通常不鼓励使用ref
参数 - 最好返回一个新值,而不是更改原始值。
答案 1 :(得分:0)
我想我已经以一种没有人理解我的意图的方式写了我的问题。我的目标是避免为需要排序的每个字段一次又一次地编写相同的.OrderBy()
/ .ThenBy()
代码(* 1)。
所以我想到了将 SortKey 添加到混音中,当 SortKey 匹配(* 2)时,我调用一个扩展方法来执行相应的调用排序(* 3)。像魅力一样。
IQueryable
扩展方法
public static System.Linq.IQueryable<TRecord> Sort<TRecord, TKey>(this System.Linq.IQueryable<TRecord> aQuery,
System.Linq.Expressions.Expression<System.Func<TRecord, TKey>> aSortFieldSelector, SortOrder aSortOrder, bool aIsPrimarySort)
{
System.Linq.IOrderedQueryable<TRecord> lOrderedQuery = aIsPrimarySort ? null : aQuery as System.Linq.IOrderedQueryable<TRecord>;
// *1
if (aSortOrder == SortOrder.Descending)
{
if (lOrderedQuery == null)
lOrderedQuery = aQuery.OrderByDescending(aSortFieldSelector);
else
lOrderedQuery = lOrderedQuery.ThenByDescending(aSortFieldSelector);
}
else
{
if (lOrderedQuery == null)
lOrderedQuery = aQuery.OrderBy(aSortFieldSelector);
else
lOrderedQuery = lOrderedQuery.ThenBy(aSortFieldSelector);
}
if (lOrderedQuery != null)
return lOrderedQuery.AsQueryable();
else
return aQuery;
}
基础课程
public class BaseClass
{
// public
public void DoSomething()
{
System.Linq.IQueryable<TRecord> lQuery = ...
System.Collections.Generic.Dictionary<string, SortOrder> lSorting = ...
...
SortQuery(lQuery, lSorting);
...
}
// protected
protected abstract void SortQuery(ref System.Linq.IQueryable<TRecord> aQuery, string aSortKey, SortOrder aSortOrder, bool aIsPrimarySort);
// private
private void SortQuery(ref System.Linq.IQueryable<TRecord> aQuery, System.Collections.Generic.Dictionary<string, SortOrder> aSorting)
{
bool lIsPrimarySort = true;
foreach (string lSortKey in aSorting.Keys)
{
SortQuery(ref aQuery, lSortKey, aSorting[lSortKey], lIsPrimarySort);
lIsPrimarySort = false;
}
}
}
派生类
public class DerivedClass : BaseClass
{
// protected
protected override void SortQuery(ref IQueryable<CouponTableRecord> aQuery, string aSortKey, SortOrder aSortOrder, bool aIsPrimarySort)
{
if (aSortKey == SortKeys.ID) // *2
aQuery = aQuery.Sort(r => r.ID, aSortOrder, aIsPrimarySort); // *3
else
if (aSortKey == SortKeys.Caption)
aQuery = aQuery.Sort(r => r.Caption, aSortOrder, aIsPrimarySort);
else
...
}
// private
private class SortKeys
{
public const string ID = "ID";
public const string Caption = "Caption";
...
}
}
关于我的问题
编译器如何解释
OrderBy
调用的确切原因 得到ORDER BY ID ASC
代替ORDER BY %Value of ID% ASC
? 编译器似乎以某种方式决定将s.ID
推导为属性名称ID
而不是采用实际的属性数据类型,因此int
值。
我发现了一个有趣的blog和一个SO answer。基本上,它是通过钻入表达式主体以获取成员名称来完成的:
Expression<Func<int>> expression = () => Sample.Foo;
MemberExpression body = (MemberExpression)expression.Body;
string name = body.Member.Name;