构建通用的逐个语句

时间:2015-12-17 10:03:46

标签: c# generics reflection lambda expression-trees

我有一个包含许多属性的类:

class Foo {
    public string Name {get; set; }
    public int Age {get; set; 
}

以及Foo的实例集合。

现在我想按用户给出的属性来排序这些元素。因此,用户从类型Foo中选择一个属性。现在我想按基于此属性的元素进行排序。

一种方法是基于反射的方法,类似于:

var p = typeof(Foo).GetProperty("Age");
var ordered = fooList.OrderBy(x => (int) p.GetValue(x, null));

到目前为止这是有效的。然而,我也尝试了第二个,在那里我被卡住了。它通过执行表达式树来处理如下:

var f = GetOrderStatement<Foo>("Age");
var ordered = fooList.OrderBy(f)

Func<T, int> GetOrderStatement<T>(string attrName)
{
    var type = Expression.Parameter(typeof(T), attrName);
    var property = Expression.PropertyOrField(type, attrName);
    return Expression.Lambda<Func<T, int>>(property).Compile();
}

我的问题是:我应该返回一个Func<T, int>从哪里获取int - 部分或者换句话说我在哪里以及如何进行实际比较?我想我必须CallExpressionIComparable.CompareTo,但我不知道该怎么做。我想我需要访问两个实例进行比较。

编辑:完整的代码示例

static void Main()
{
    var fooList = new[] { new Foo("Hans", 10), new Foo("Georg", 12), new Foo("Birgit", 40) };
    var f = GetOrderStatement<Foo>("Age");
    var ordered = fooList.OrderBy(f);
}

private static Func<T, int> GetOrderStatement<T>(string attrName)
{
    var type = Expression.Parameter(typeof(T), attrName);
    var property = Expression.PropertyOrField(type, attrName);
    return Expression.Lambda<Func<T, int>>(property).Compile();
}

执行此代码将抛出

  

ArgumentException:为lambda提供的参数数量不正确   声明

1 个答案:

答案 0 :(得分:2)

问题是您正在尝试构建Func<T, int>,但是您对Expression.Lambda的调用并未指定参数表达式,这意味着您无法预期创建具有任何参数的委托。只需将type指定为Expression.Lambda的第二个参数即可。以下是基于您的问题的完整示例 - 请注意,我已经更改了年龄以证明它实际订购了,并且我已将您的字段更新为只读属性:

using System;
using System.Linq;
using System.Linq.Expressions;

class Foo 
{
    public string Name { get; }
    public int Age { get; }

    public Foo(string name, int age)
    {
        this.Name = name;
        this.Age = age;
    }
}

class Test
{
    static void Main()
    {
        var fooList = new[]
        {
            new Foo("Hans", 12),
            new Foo("Georg", 10),
             new Foo("Birgit", 40)
        };
        var f = GetOrderStatement<Foo>("Age");
        var ordered = fooList.OrderBy(f);
        foreach (var item in ordered)
        {
            Console.WriteLine($"{item.Name}: {item.Age}");
        }
    }

    private static Func<T, int> GetOrderStatement<T>(string attrName)
    {
        var type = Expression.Parameter(typeof(T), attrName);
        var property = Expression.PropertyOrField(type, attrName);
        return Expression.Lambda<Func<T, int>>(property, type).Compile();
    }
}

输出:

Georg: 10
Hans: 12
Birgit: 40