【问题标题】:Enumerable Select by Expression Tree表达式树的可枚举选择
【发布时间】:2015-09-12 20:37:32
【问题描述】:

我正在研究“表达式树”,但我无法执行这些表达式:

// first case
someList.Select(p => p.SomeProperty);

// second case
someList.Select(p => new OtherClass 
{
    SomeProperty = p.SomeProperty
})

对于我尝试这样做的“第一种情况”:

var someList = new List<SomeClass>();
someList.Add(new SomeClass { SomeProperty = "Hello" });

var someParam = Expression.Parameter(typeof(SomeClass), "p");
var someProperty = Expression.Property(someParam, "SomeProperty");

Expression.Call(
    typeof(Enumerable),
    "Select",
    new Type[]
    {
        typeof(SomeClass),
        typeof(string)
    },
    Expression.Lambda(
        someProperty,
        someParam
    )
).Dump();

但我收到此错误:

InvalidOperationException:类型“System.Linq.Enumerable”上没有通用方法“Select”与提供的类型参数和参数兼容。如果方法是非泛型的,则不应提供类型参数。

关于“第二种情况”,我不知道如何进行。

有人可以在这里指导我吗?

【问题讨论】:

  • 我认为您缺少在 Expression.Call 中提供介于 new Type[]Expression.Lambda 之间的 IEnumerable 参数
  • 您不能直接使用带有Enumerable.* 方法的表达式树。你必须.Compile() 他们,或者使用IQueryable&lt;&gt;Queryable.* 方法。

标签: c# lambda expression expression-trees enumerable


【解决方案1】:

冷静下来,经过一些研究,我发现我的代码中缺少什么......

第一个案例:

Expression.Call(
    typeof(Enumerable),
    "Select",
    new Type[]
    {
        typeof(SomeClass),
        typeof(string)
    },
    Expression.Constant(someList), // <---------------- HERE IT IS
    Expression.Lambda(
        someProperty,
        someParam
    )
);

对于第二种情况,我通过下面的代码创建了“新”表达式:

var bind = Expression.Bind(typeof(OtherClass).GetProperty("SomeProperty"), someProperty);
var otherClassNew = Expression.New(typeof(OtherClass));
var otherClassInit = Expression.MemberInit(otherClassNew, bind);

无论如何,谢谢大家的帮助!

【讨论】:

    【解决方案2】:

    你可以做的一些例子:

    给定

    public class SomeClass
    {
        public string SomeProperty { get; set; }
    }
    

    var someList = new List<SomeClass>();
    someList.Add(new SomeClass { SomeProperty = "Hello" });
    
    var someParam = Expression.Parameter(typeof(SomeClass), "p");
    var someProperty = Expression.Property(someParam, "SomeProperty");
    
    Expression<Func<SomeClass, string>> lambda = Expression.Lambda<Func<SomeClass, string>>(someProperty, someParam); // p => p.SomeProperty
    

    使用IEnumerable&lt;SomeClass&gt;...注意.Compile()

    Func<SomeClass, string> compiled = lambda.Compile();
    IEnumerable<string> q1 = someList.Select(compiled);
    

    您不应该永远在单元测试和“实验”程序(比如这个)中使用AsQueryable()而是。为了让@Peter 开心,我将添加另一个可能的条件:如果您真的知道它的作用(不是认为您知道它的作用,真的!),那么你就可以使用它了。但是如果是第一次用,还是建议你在SO上问问自己用对了没有。

    IQueryable<SomeClass> queryable = someList.AsQueryable();
    

    直接使用Queryable.Select()

    IQueryable<string> q2 = queryable.Select(lambda);
    

    构建一个Select 并使用CreateQuery(这与Queryable.Select 在内部所做的非常相似)在查询中“注入”它。

    MethodInfo select = (from x in typeof(Queryable).GetMethods()
                        where x.Name == "Select" && x.IsGenericMethod
                        let gens = x.GetGenericArguments()
                        where gens.Length == 2
                        let pars = x.GetParameters()
                        where pars.Length == 2 && 
                            pars[0].ParameterType == typeof(IQueryable<>).MakeGenericType(gens[0]) &&
                            pars[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(gens))
                        select x).Single().MakeGenericMethod(typeof(SomeClass), typeof(string));
    
    MethodCallExpression select2 = Expression.Call(null, select, Expression.Constant(queryable), lambda);
    
    IQueryProvider provider = queryable.Provider;
    IQueryable<string> q3 = provider.CreateQuery<string>(select2);
    

    【讨论】:

    • 为什么我们应该“从不使用 AsQueryable() 但在单元测试和实验程序中”?鉴于如此强烈的声明,如果你用简短的解释来支持它会很好
    • @PeterLillevold 因为它没有像 90% 的 C# 程序员认为的那样做。如果有人在单元测试之外使用它,那么 10 次中有 9 次他不知道自己在做什么,只是在编译之前扔东西。
    • @PeterLillevold 正确的句子应该是“你不应该使用它,除非你知道你在做什么”,但很少有人会承认他们在编译之前他们正在扔东西,所以它更容易使用绝对...比如“不要使用floatdouble,除非你是游戏程序员或正在创建PDF”。
    • 好的,因此我认为您的陈述高度自以为是,并且没有突出任何与 OP 问题相关的内容。除非你有充分的理由支持它,否则使用绝对不会增加意识和理解。
    • @PeterLillevold 这个问题与AsQueryable() 无关,我想明确表示我不赞成在任何地方使用它,而是在某个地方使用它。现在......可能有一些用例可以使用AsQueryable(),但您必须“这么高”才能识别它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    相关资源
    最近更新 更多