使用 lambda 表示法时,C# 中如何解释通用 TKey?
How is generic TKey interpreted in C# when using lambda notation?
我想在基础 class 中使用一些 System.Linq
扩展方法,而派生的 classes 应该能够通过覆盖特定方法来提供一些表达式。
当前基地class代码:
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
}
编译器输出错误
Cannot implicitly convert type 'int' to '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 值。
编辑2
好的,+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);
}
How does OrderBy
infer what the types of TSource
and TKey
are
编译器可以通过查看以下内容来推断 TSource
和 TKey
的类型:
lQuery
的类型 - 在这种情况下,IQueryable<{your class}>
- return 表达式的类型,在本例中,
ID
属性 of {your class}
的类型
How to resolve the compiler error?
您要求 调用者 指定 TKey
的类型(通过泛型参数),但在您的方法中您指定的是 已知类型。您可以删除通用参数:
protected void SetOrder(IQueryable<%type of s%> aQuery)
{
Expression<Func<%type of s%, int>> lKeySelector = s => s.ID;
aQuery.OrderBy(lKeySelector);
}
但请注意 OrderBy
returns 查询 - 它不会 更改 原始查询,所以你真正想要的是:
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
参数 - 最好 return 一个新值而不是更改原始值。
我想我以一种没有人理解我的意图的方式写下了我的问题。我的目标是避免为每个需要排序的字段重复编写相同的 .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;
}
基础class
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;
}
}
}
派生class
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";
...
}
}
关于我的问题
How exactly is that OrderBy
call interpreted by the compiler as it
results in an ORDER BY ID ASC
instead of ORDER BY %Value of ID% ASC
?
The compiler seems to somehow decide to deduce s.ID
to property name
ID
instead of taking the actual property data type and thus the int
value.
我发现了一个有趣的 blog and also a 。基本上它是通过钻入表达式主体来获取成员名称来完成的:
Expression<Func<int>> expression = () => Sample.Foo;
MemberExpression body = (MemberExpression)expression.Body;
string name = body.Member.Name;
我想在基础 class 中使用一些 System.Linq
扩展方法,而派生的 classes 应该能够通过覆盖特定方法来提供一些表达式。
当前基地class代码:
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
}
编译器输出错误
Cannot implicitly convert type 'int' to '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 值。
编辑2
好的,+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);
}
How does
OrderBy
infer what the types ofTSource
andTKey
are
编译器可以通过查看以下内容来推断 TSource
和 TKey
的类型:
lQuery
的类型 - 在这种情况下,IQueryable<{your class}>
- return 表达式的类型,在本例中,
ID
属性 of{your class}
的类型
How to resolve the compiler error?
您要求 调用者 指定 TKey
的类型(通过泛型参数),但在您的方法中您指定的是 已知类型。您可以删除通用参数:
protected void SetOrder(IQueryable<%type of s%> aQuery)
{
Expression<Func<%type of s%, int>> lKeySelector = s => s.ID;
aQuery.OrderBy(lKeySelector);
}
但请注意 OrderBy
returns 查询 - 它不会 更改 原始查询,所以你真正想要的是:
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
参数 - 最好 return 一个新值而不是更改原始值。
我想我以一种没有人理解我的意图的方式写下了我的问题。我的目标是避免为每个需要排序的字段重复编写相同的 .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;
}
基础class
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;
}
}
}
派生class
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";
...
}
}
关于我的问题
How exactly is that
OrderBy
call interpreted by the compiler as it results in anORDER BY ID ASC
instead ofORDER BY %Value of ID% ASC
? The compiler seems to somehow decide to deduces.ID
to property nameID
instead of taking the actual property data type and thus theint
value.
我发现了一个有趣的 blog and also a
Expression<Func<int>> expression = () => Sample.Foo;
MemberExpression body = (MemberExpression)expression.Body;
string name = body.Member.Name;