使用Dynamic Linq库运行总和

时间:2015-11-05 23:22:16

标签: c# linq dynamic-linq

给出以下列表:

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将返回任意记录
  • 出于同样的原因,每个项目必须是唯一的
  • 这与第二个C#示例
  • 的效率相同

有没有办法使用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的问题。

1 个答案:

答案 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;
}