c# - 如何使用类型变量的类型参数创建表达式<Func<>>

标签 c# linq types expression

我想写一个像下面这样的语句:

Expression<Func<AClass, bool>> filter = x => true;

除了 AClass 之外,我想使用在运行时确定的 Type 变量。所以,概念上是这样的:

Type aClassType = methodParameter.GetType();
Expression<Func<aClassType, bool>> filter = x => true;

显然语法会有很大的不同。我假设我需要使用某种反射或其他幻想。

最终目标

这里的最终目标有点复杂,因此我对上面的示例进行了极大的简化。将要使用此委托(delegate)的实际 .Where 调用看起来更像这样:

var copy = iQ;

...

copy = copy.Where( p1 => iQ.Where( p2 => pgr2.Key == p1.Key && p2.DataField == column.DataField && p2.ColumnText.Contains( requestValue ) ).Any() );

p1 和 p2 的所有属性都是 IQueryable iQ 元素类型的父类的属性。我要创建的 Type 变量将是 iQ 的实际元素类型,即子类。

我该怎么做?

目前的进展

根据以下答案,我编写了这段测试代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace IQueryableWhereTypeChange {
    class Program {
        static void Main( string[] args ) {
            var ints = new List<ChildQueryElement>();

            for( int i = 0; i < 10; i++ ) {
                ints.Add( new ChildQueryElement() { Num = i, Value = i.ToString() } );
            }

            IQueryable<ChildQueryElement> theIQ = ints.AsQueryable();

            Type type = typeof(ChildQueryElement);
            var body = Expression.Constant(true);
            var parameter = Expression.Parameter(type);
            var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(Boolean));
            var lambda = Expression.Lambda( delegateType, body, parameter );

            Console.WriteLine( lambda.GetType() );

            dynamic copy = theIQ;

            Type copyType1 = copy.GetType().GetGenericArguments()[ 0 ];
            Type elementType1 = ((IQueryable)copy).ElementType;
            Console.WriteLine( "copyType1 : " + copyType1.ToString() );
            Console.WriteLine( "elementType1 : " + elementType1.ToString() );

            copy = Queryable.Where( copy, lambda );

            Type copyType2 = copy.GetType().GetGenericArguments()[ 0 ];
            Type elementType2 = ((IQueryable)copy).ElementType;
            Console.WriteLine( "copyType2 : " + copyType2.ToString() );
            Console.WriteLine( "elementType2 : " + elementType2.ToString() );
        }
    }

    public class ParentQueryElement {
        public int Num { get; set; }
    }
    public class ChildQueryElement : ParentQueryElement {
        public string Value { get; set; }
    }
}

那个程序有这样的输出:

System.Linq.Expressions.Expression`1[System.Func`2[IQueryableWhereTypeChange.ChildQueryElement,System.Boolean]]  
copyType1 : IQueryableWhereTypeChange.ChildQueryElement  
elementType1 : IQueryableWhereTypeChange.ChildQueryElement  

Unhandled Exception:  

    Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 
        The best overloaded method match for 
            'System.Linq.Queryable.Where<IQueryableWhereTypeChange.ChildQueryElement>(
                System.Linq.IQueryable<IQueryableWhereTypeChange.ChildQueryElement>,
                System.Linq.Expressions.Expression<System.Func<IQueryableWhereTypeChange.ChildQueryElement,bool>>
            )' 
        has some invalid arguments
   at CallSite.Target(Closure , CallSite , Type , Object , LambdaExpression )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
   at IQueryableWhereTypeChange.Program.Main(String[] args)

在我尝试复制我的复杂谓词之前,我希望它能正常工作。

我发现异常令人费解,因为 lambda.GetType() 的输出几乎与异常中的类型完全匹配。唯一的区别是 System.Booleanbool,但这无关紧要。

最佳答案

您需要创建适当的委托(delegate)类型,然后将其传递给 Expression.Lambda方法。例如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

class Test
{
    static void Main()
    {
        var type = typeof(string);
        var body = Expression.Constant(true);
        var parameter = Expression.Parameter(type);
        var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(bool));
        dynamic lambda = Expression.Lambda(delegateType, body, parameter);
        Console.WriteLine(lambda.GetType()); // Expression<string, bool>
    }
}

现在当然是你的 body通常不会只是一个常数 - 但我们不知道你需要在那里做什么。从你编辑的问题来看,你似乎确实有一些类型的静态类型知识,否则你将无法表达那个 lambda 表达式。因此,您要么需要手动构建表达式树以等效于 lambda 表达式(使用 Expression.Property 等),要么根据您所知道的创建一个表达式树,然后使用表达式树访问者使其适应实际类型.

编辑:注意 lambda 的类型必须是 dynamic让它作为 Queryable.Where 的第二个参数,否则 C# 编译器的执行时版本将使用变量的静态类型(即 LambdaExpression )来约束重载解析。您希望它匹配实际的 Expression<TDelegate>类型,你不能在编译时表达,所以你需要使用 dynamic .

关于c# - 如何使用类型变量的类型参数创建表达式<Func<>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25793736/

相关文章:

c# - 是否可以将大字符串写入 Firebird blob?

c# - 添加到初始化的多维槽

c# - 排序字典时出错

c - Windows:BOOL 有多大?

c# - 从 Excel 单元格中捕获时间值

c# - c# 中不区分大小写的 XML 解析器

c# - Linq 通过变量访问属性

java - java中奇怪的东西

java - 根据输入字符串决定 var 类型

c# - 文本文件到字典