给出以下列表:
var items = new List<int> { 1, 2, 3, 5 };
我正在尝试使用Dynamic Linq library计算任何给定记录的以前记录总数。在标准LINQ(LINQ to Objects)中,我可以这样做:
var runningTotal = 0;
var qry1 = items.Select(x => {
var ret = new { x, runningTotal };
runningTotal += x;
return ret;
});
或者这个(效率较低,因为它每次都会迭代列表中的所有先前项目。对于这么小的列表来说这显然不是问题,但是对于较大的列表来说这是一个问题):
var qry1 = items.Select((x, idx) => new {
x,
runningTotal = items.Take(idx).Sum()
});
库不会在字符串表达式中实现Take
,也不会在Select
的重载中实现获取元素的索引。但是,以下内容几乎可以实现:
var qry2 = items.AsQueryable().Select(
"new(it as x, @0.Where(it < parent).Sum(it) as runningTotal)",
items
);
但是,这有一些缺点:
Where
将返回任意记录有没有办法使用Dynamic Linq,没有这些问题,如果是,那么如何?
更新
我尝试写下面的课程:
[DynamicLinqType]
public class Aggregator<T> {
private T state;
private Func<T, T, T> fn;
public Aggregator(Func<T, T, T> fn) {
this.fn = fn;
}
public T GetState(T value) {
state = fn(state, value);
return state;
}
}
然后像这样查询:
var aggregator = new Aggregator<int>((runningTotal1, x) => runningTotal1 + x);
var qry4 = items.AsQueryable().Select(
"new(it as x, @0.GetState(it) - it as runningTotal)",
aggregator
);
但即使ParseException: Methods on type 'Aggregator1' are not accessible
方法是公开的,我也会获得GetState
。这是因为库限制自己仅使用某些预定义类型的成员,以及标有DynamicLinqTypeAttribute
的类型(默认情况下)。但标记的类型Aggregator<T>
与构造的类型(Aggregator<int>
)不同。
更新2
我已提交了issue about running calculations和另一个关于generic combinations of recognized types的问题。
答案 0 :(得分:2)
我做了一些研究,发现错误在哪里:
DynamicLinqType
注册类型 - 在您的情况下Aggregator<T>
因此,当您将T
指定为 int 时,这已经是另一种类型Aggregator<int>
,并且DynamicLinq
认为它不是预先键入的类型。
因此,作为解决方案,您只需删除通用部分:
[DynamicLinqType]
public class Aggregator
{
private int state;
private Func<int, int, int> fn;
public Aggregator(Func<int, int, int> fn)
{
this.fn = fn;
}
public int GetState(int value)
{
state = fn(state, value);
return state;
}
}
<强>更新强>
还有另外一种方法 - 在源代码中解决这个问题:
static bool IsPredefinedType(Type type)
{
if (_predefinedTypes.Contains(type)) return true;
if (GlobalConfig.CustomTypeProvider.GetCustomTypes().Contains(type)) return true;
// for generic type check GenericTypeDefinition
if (type.IsGenericType && GlobalConfig.CustomTypeProvider.GetCustomTypes().Contains(type.GetGenericTypeDefinition())) return true;
return false;
}