Using Lambda Expressions trees with IEnumerable
I've been trying to learn more about using Lamba expression trees and so I created a simple example. Here is the code, this works in LINQPad if pasted in as a C# program.
void Main()
{
IEnumerable<User> list = GetUsers().Where(NameContains("a"));
list.Dump("Users");
}
// Methods
public IEnumerable<User> GetUsers()
{
yield return new User{Name = "andrew"};
yield return new User{Name = "rob"};
yield return new User{Name = "chris"};
yield return new User{Name = "ryan"};
}
public Expression<Func<User, bool>> NameContains(string namePart)
{
return u => u.Name.Contains(namePart);
}
// Classes
public class User
{
public string Name { get; set; }
}
This results in the following error:
The type arguments for method 'System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
However if I just substitute the first line in main with thi开发者_如何学Gos:
IEnumerable<User> list = GetUsers().Where(u => u.Name.Contains("a"));
It works fine. Can tell me what I'm doing wrong, please?
The Enumerable.Where
method takes a Func<T, bool>
, not an Expression<Func<T, bool>>
. Perhaps you're confusing with Queryable.Where
, which does take an expression as a parameter... In your case you don't need an expression, you just need a delegate that can be executed against each item in the sequence. The purpose of expressions is (mostly) to be analysed and translated to something else (SQL for instance), to perform the query against an external data source
Change the return type of NameContains
from Expression<Func<User, Bool>>
to simply Func<User, Bool>
. In this situation, there's no need to return the Expression, you actually want to return the compiled delegate. There's a difference between the expression that makes up the lambda, and the lambda (which is a delegate) itself.
If you send a lambda into a method, the method can accept the lambda either as an expression, or as a compiled delegate type, depending on what you specify in the parameters. If the incoming parameter type is an Expression, you can send in something that looks like a delegate, however, if the method is expecting a delegate, you have to give it a compiled delegate, not simply an expression. That being said, you can also do something like:
var certainUsers = GetUsers().Where(NameContains("a").Compile());
Which would compile the expression, and return a Func<User, Bool>
.
Lambda expressions can be treated as either code (delegates) or as data (expression trees)
In your example your are attempting to treat the lambda expression as code.
You would use the Expression<> declaration when you want to treat the lambda expression as data.
Why would you want to do this?
Here is a quote from the Book Linq In Action,
" Expression trees can be given to tools at runtime, which use them to guide their execution or translate them into something else, such as SQL in the case of LINQ to SQL."
Using Expression Trees allows you to take the lambda expression and convert it to data, this is how Linq to SQL works, it takes the lambda expression or query operators or query expressions and converts them to SQL. You of course can view and modify the created expression tree once converted to sql.
There is a huge difference between Expression and Func<...>, the Func is a pure delegate you can invoke it directly, the expression is a data structure holds information about an expression like information about lambda expression or Linq Syntax (e.g. From x in list where x.Id = 1 select x). The expression cannot be invoked directly it must be compiled first, Expressions is used to convert the expression from a way to another like Link To Sql which converts an expression to Sql statements, the best way to do this to change the return type of the NameContains Method to Func insted of expression cuz you are working with Linq To Objects, but when using with Linq To Sql you can use both Expression or func.
精彩评论