我想写一个如下声明:
Expression<Func<AClass, bool>> filter = x => true;
除了代替AClass
,我想使用在运行时确定的Type
变量。所以,概念上是这样的:
Type aClassType = methodParameter.GetType();
Expression<Func<aClassType, bool>> filter = x => true;
显然语法会有所不同。我假设我需要使用某种反思或其他幻想。
这里的最终目标有点复杂,所以我为上面的例子极大地简化了事情。要使用此委托的实际.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.Boolean
与bool
,但这无关紧要。
答案 0 :(得分:13)
您需要创建适当的委托类型,然后将其传递给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
。
答案 1 :(得分:1)
您需要创建一个表达式,其主体设置为常量true
,并且您需要创建类型类型的参数。有Expression
来做这些事情:
Type type = typeof(object);
var lambda = Expression.Lambda(
Expression.Constant(true),
Expression.Parameter(type, "parameter"));
答案 2 :(得分:-1)
你不能将Type作为泛型参数传递,这不是泛型的概念,你想要的只是一个方法的参数。