C#lambda如何工作?

问题描述 投票:3回答:7

我正在尝试实现搜索数据库的方法。

我忘了提到我正在使用Postgresql,所以我不能使用内置的LINQ to SQL。

我希望它是那样的:

var user = User.Find(a => a.LastName == "Brown");

就像它在List类中完成的一样。但是当我去List的源代码(谢谢,Reflector)时,我看到了这个:

public T Find(Predicate<T> match)
{
    if (match == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        if (match(this._items[i]))
        {
            return this._items[i];
        }
    }
    return default(T);
}

我怎么能实现这个呢?我需要获取这些参数来进行搜索。

好吧,我现在明白我需要做LINQ to SQL来做所有这些好的表达式的东西,否则我将不得不花费大量时间重新完成轮子。

由于我不能使用LINQ to SQL,我实现了这个简单的方法:

public static User Find(User match, string orderBy = "")
    {
        string query = "";
        if (!String.IsNullOrEmpty(match.FirstName)) query += "first_name='" + match.FirstName + "'";
        if (!String.IsNullOrEmpty(match.LastName)) query += "last_name='" + match.LastName+ "'";
        return Find(query + (!String.IsNullOrEmpty(orderBy) ? orderBy : ""));
    }

这是如何使用它:

var user = User.Find(new User { FirstName = "Bob", LastName = "Brown" });
c# lambda find
7个回答
6
投票

你的方法应该接受Expression<Func<User>>

这将为您提供表达式树而不是委托,您可以分析和序列化为SQL或转换为您的数据库具有的任何其他API调用。

如果您希望所有内容都是通用的,您可能希望继续实现IQueryable接口。有用的信息可以在这里找到:LINQ Tips: Implementing IQueryable Provider

虽然对于一个简单的场景,我建议不要复杂化所有内容并坚持使用表达式树并返回普通的IEnumerable<T>甚至List<T>

对于您的情况,第一版代码可能如下所示:

public IEnumerable<T> Get(Expression<Func<T, bool>> condition)
{
    if (condition.Body.NodeType == ExpressionType.Equal)
    {
        var equalityExpression = ((BinaryExpression)condition.Body);

        var column = ((MemberExpression)equalityExpression.Left).Member.Name;

        var value = ((ConstantExpression)equalityExpression.Right).Value;

        var table = typeof(T).Name;

        var sql = string.Format("select * from {0} where {1} = '{2}'", table, column, value);

        return ExecuteSelect(sql);
    }

    return Enumerable.Empty<T>();
}

当您想要处理新的和新的场景时,它的复杂性会快速增长,因此请确保您为每个场景都有可靠的单元测试。

C# Samples for Visual Studio 2008包含ExpressionTreeVisualizer,它将帮助您更轻松地深入了解表达式树,以了解如何从中提取所需的信息。

当然,如果您坚持使用现有的LINQ实现,我建议您这样做。 SQL Server数据库有Linq to SQL,许多不同数据库有Linq to Entities,NHbernate项目有Linq to NHibernate

许多其他LINQ提供商可以在这里找到:Link to Everything: A List of LINQ Providers。实现LINQ提供程序的工作量并不重要,因此重用经过测试和支持的解决方案是个好主意。


2
投票

完全一样的方式。只需将this._items替换为您的用户集合。

还要使用User类型替换类型参数T.


1
投票

源代码中的lambda表达式可以在编译时转换为已编译的可执行委托或表达式树。通常我们将lambda与委托关联,但在你的情况下,因为你说你想要访问参数(在这种情况下,我假设你的意思是LastName"Brown"然后你想要一个表达式树。

一旦你有了一个表达式树,你就可以解析它以确切地看到它是什么,将它翻译成你实际需要做的事情。

这里有一些关于表达树的问题。

Expression trees for dummies?

Bit Curious to understand Expression Tree in .NET

听起来你肯定会重新发明一个非常复杂的车轮。我确信这将是一次有用的学习体验,但您应该查看LINQ to Entities或LINQ to SQL以进行实际编程。


1
投票

也许我只是没有理解这个问题,但已经有了一种方法可以做你想做的事:Enumerable.Where

如果您需要找到单个元素,请改用SingleOrDefaultFirstOrDefault


1
投票

你可以这样做:

public static IEnumerable<User> Find(Predicate<User> match)
{
    //I'm not sure of the name
    using (var cn = new NpgsqlConnection("..your connection string..") )
    using (var cmd = new NpgsqlCommand("SELECT * FROM Users", cn))
    using (var rdr = cmd.ExecuteReader())
    {
        while (rdr.Read())
        {
           var user = BuildUserObjectFromIDataRecord(rdr);
           if (match(user)) yield return user;
        }
    }
}

然后你就可以这样称呼它

var users = User.Find(a => a.LastName == "Brown");

请注意,这将返回任意数量的用户,您仍然必须实现BuildUserObjectFromIDataRecord()函数,并且它总是希望迭代整个users表。但它为您提供了您想要的确切语义。


0
投票

好吧,我现在明白我需要做LINQ to SQL来做所有这些好的表达式的东西,否则我将不得不花费大量时间重新完成轮子。

由于我不能使用LINQ to SQL,我实现了这个简单的方法:

public static User Find(User match, string orderBy = "")
    {
        string query = "";
        if (!String.IsNullOrEmpty(match.FirstName)) query += "first_name='" + match.FirstName + "'";
        if (!String.IsNullOrEmpty(match.LastName)) query += "last_name='" + match.LastName+ "'";
        return Find(query + (!String.IsNullOrEmpty(orderBy) ? orderBy : ""));
    }

这是如何使用它:

var user = User.Find(new User { FirstName = "Bob", LastName = "Brown" });

-1
投票

一种方法是创建一个匿名委托,如下所示:

Predicate<User> Finder = delegate(User user)
{
    return user.LastName == "Brown";
}

var User = User.Find(Finder);
© www.soinside.com 2019 - 2024. All rights reserved.