2009-05-25 58 views
-2

我必须创建一个表达式树来表示用户输入的查询,这有点难度。由于我没有时间创建所有可能的用户输入案例,因此我认为表达式树会帮助我解决这个问题。表达式与谓词问题

大多数情况下,它有。但是,我有点难住。我在下面的代码中试图用动态创建的表达式来执行List.Find。表达,总之,是这样的:

list.Find(m => m.ListOfStrings.Exists(s => s == "cookie")); 

其中m为

class MyClass 
{ 
    public List<string> ListOfStrings { get; set; } 
} 

竟然创造

s => s == "cookie" 

与表情,没有任何问题,我已经得到了。我也宣布MethodInfo的对已存在

var existsMethod = typeof(MyClass) 
     .GetProperty("ListOfStrings") 
     .PropertyType 
     .GetMethod("Exists"); 

我唯一的问题是建立一个表达的lambda来调用该方法作为一个参数,像这样

var findLambda = Expression.Lambda(
    Expression.Call(
     Expression.Property(
      Expression.Parameter(typeof(MyClass), "m"), 
      typeof(MyClass).GetProperty("ListOfStrings")), 
     existsMethod, 
     existsLambda), 
    Expression.Parameter(
     typeof (MyClass), 
     "m")); 

它给出了一个可以理解的异常

Expression of type 'System.Func`2[System.String,System.Boolean]' cannot be used for parameter of type 'System.Predicate`1[System.String]' of method 'Boolean Exists(System.Predicate`1[System.String])' 

我怎么能克服这个?

全码:

private class MyClass 
{ 
    public List<string> ListOfStrings { get; set; } 
} 

public void SomeMethod() 
{ 
    var myObject = new MyClass(); 
    myObject.ListOfStrings = new List<string>(); 
    myObject.ListOfStrings.Add("cookie"); 
    myObject.ListOfStrings.Add("biscuit"); 

    List<MyClass> list = new List<MyClass>(); 
    list.Add(myObject); 

    var existsLambda = Expression.Lambda(
     Expression.Equal(
      Expression.Parameter(typeof(string), "s"), 
      Expression.Constant("cookie")), 
     Expression.Parameter(typeof(string), "s")); 

    var existsMethod = typeof(MyClass).GetProperty("ListOfStrings").PropertyType.GetMethod("Exists"); 

    var findLambda = Expression.Lambda(
     Expression.Call(
      Expression.Property(
       Expression.Parameter(typeof(MyClass), "m"), 
       typeof(MyClass).GetProperty("ListOfStrings")), 
      existsMethod, 
      existsLambda), 
     Expression.Parameter(
      typeof (MyClass), 
      "m")); 

    list.Find((Predicate<MyClass>)findLambda.Compile()); 
} 
+0

我知道,“动态”用户输入的是一个有点Linq中的挑战,但如果用户只需指定要过滤的字段的不同值或同一元素上的不同过滤器,您可以将Where子句附加到原始过滤器,并在过滤器本身中使用输入。所以简而言之:完全不清楚为什么你要经历所有这些麻烦,以及为什么你不能在必要时追加Where子句。 – 2009-05-25 16:18:36

+0

@Frans,因为我不能简单地让用户为静态定义的字段指定不同的值。我需要一个完全动态的解决方案来a)减少批量 - 需要维护的一些代码,c)在所有这些下面的类库中提供新类的可扩展性。 – 2009-05-25 18:21:45

回答

2

代表们具有不同的类型:

public delegate bool Predicate<T>(T obj); 
public delegate TResult Func<T, TResult>(T arg); 

Exists方法(以及Find)期望Predicate<T>。 Lambda表达式在运行时编译为Func<T, TResult>

尝试以下操作:

var existsLambda = Expression.Lambda(typeof(Predicate<string>), 
       Expression.Equal(
        Expression.Parameter(typeof(string), "s"), 
        Expression.Constant("cookie")), 
       Expression.Parameter(typeof(string), "s")); 

您也可以使用通用Lambda Function

var existsLambda = Expression.Lambda<Predicate<string>>(Expression.Equal(
        Expression.Parameter(typeof(string), "s"), 
        Expression.Constant("cookie")), 
       Expression.Parameter(typeof(string), "s")); 
+0

是的,我知道它们是不同的类型,我只是认为它足够聪明,可以将Func 变成谓词,因为谓词返回bool,毕竟...我会试试这个我重新开始工作 - 谢谢! – 2009-05-25 18:17:58

+0

我也知道它在运行时编译了什么,但问题是我无法发送谓词作为参数,因为Expression.Call需要一个表达式 - 您的代码似乎可以解决该问题。就像我说的,明天我会试一试。再次感谢。 =) – 2009-05-25 18:19:37

+0

太棒了。这样一个简单的改变。谢谢。 =) – 2009-05-26 08:50:56

0

如果你看一下邮件,它会告诉你谓词是不兼容Func键。现在

,谓语被定义为这样的:

public delegate bool Predicate<T>(
    T obj 
) 

,你必须Func键这样:

public delegate TResult Func<T, Result>(
    T arg1 
) 

放在一起,你试图让这些代表2兼容:

public delegate bool MyClassPredicate (MyClass obj) 
public delegate bool StringFunc (string arg1) 

即, string!= MyClass。

我希望它有道理。

+0

这与我的错误没有任何关系。 =) – 2009-05-25 18:17:06

+0

你是对的,我完全设法读到那里都错了,那就说,试试布鲁诺的建议吧。 – kastermester 2009-05-25 21:29:02