【问题标题】:How might I clean up this lambda?我该如何清理这个 lambda?
【发布时间】:2011-09-02 19:13:31
【问题描述】:

我有一个表达式,我在几个 LINQ 查询中使用了几次,所以我将它分离到它自己的返回表达式的方法中。函数的 lambda 部分看起来有点乱。有人想尝试重构它并使其更具可读性和/或更小吗?

    private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
    {
        return x => ((sent ? x.FromUser : x.ToUser).Username.ToLower() == username.ToLower()) && (sent ? !x.SenderDeleted : !x.RecipientDeleted);
    }

简单的英文描述是它正在检查布尔值 sent 并根据该布尔值检查 Message.FromUserMessage.ToUser

如果用户正在查看他/她的发件箱,sent 为真,它将查看x.FromUser.Username == usernamex.SenderDeleted == false

如果用户正在查看他/她的收件箱,那么它会执行相同的逻辑,但发送的是 false,它会检查 x.ToUserx.RecipientDeleted

也许这是最简单的方法,但我愿意进行一些重构。

回答编辑

我真的很喜欢 Davy8 的回答,但我决定更进一步,用嵌套函数做两个表达式,而不是一个表达式。现在我有以下方法:

    /// <summary>
    /// Expression to determine if a message belongs to a user.
    /// </summary>
    /// <param name="username">The name of the user.</param>
    /// <param name="sent">True if retrieving sent messages.</param>
    /// <returns>An expression to be used in a LINQ query.</returns>
    private Expression<Func<Message, bool>> MessageBelongsToUser(string username, bool sent)
    {
        return x => (sent ? x.FromUser : x.ToUser).Username.Equals(username, StringComparison.OrdinalIgnoreCase);
    }

    /// <summary>
    /// Expression to determine if a message has been deleted by the user.
    /// </summary>
    /// <param name="username">The name of the user.</param>
    /// <param name="sent">True if retrieving sent messages.</param>
    /// <returns>An expression to be used in a LINQ query.</returns>
    private Expression<Func<Message, bool>> UserDidNotDeleteMessage(string username, bool sent)
    {
        return x => sent ? !x.SenderDeleted : !x.RecipientDeleted;
    }

所以现在我的查询如下所示:

    /// <summary>
    /// Retrieves a list of messages from the data context for a user.
    /// </summary>
    /// <param name="username">The name of the user.</param>
    /// <param name="page">The page number.</param>
    /// <param name="itemsPerPage">The number of items to display per page.</param>
    /// <param name="sent">True if retrieving sent messages.</param>
    /// <returns>An enumerable list of messages.</returns>
    public IEnumerable<Message> GetMessagesBy_Username(string username, int page, int itemsPerPage, bool sent)
    {
        var query = _dataContext.Messages
            .Where(MessageBelongsToUser(username, sent))
            .Where(UserDidNotDeleteMessage(username, sent))
            .OrderByDescending(x => x.SentDate)
            .Skip(itemsPerPage * (page - 1))
            .Take(itemsPerPage);
        return query;
    }

    /// <summary>
    /// Retrieves the total number of messages for the user.
    /// </summary>
    /// <param name="username">The name of the user.</param>
    /// <param name="sent">True if retrieving the number of messages sent.</param>
    /// <returns>The total number of messages.</returns>
    public int GetMessageCountBy_Username(string username, bool sent)
    {
        var query = _dataContext.Messages
            .Where(MessageBelongsToUser(username, sent))
            .Where(UserDidNotDeleteMessage(username, sent))
            .Count();
        return query;
    }

我会说这些是一些非常英语可读的查询,谢谢大家!

参考:http://www.codetunnel.com/blog/post/64/how-to-simplify-complex-linq-expressions

【问题讨论】:

标签: c# linq lambda linq-to-entities expression


【解决方案1】:

拆分成一个单独的函数:

    private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
    {
        return message=> InnerFunc(message, username, sent);
    }

    private static bool InnerFunc(Message message, string username, bool sent)
    {
        if(sent)
        {
            return string.Equals(message.FromUser.Username, username, StringComparison.InvariantCultureIgnoreCase) && !message.SenderDeleted;
        }
        return string.Equals(message.ToUser.Username, username, StringComparison.InvariantCultureIgnoreCase) && !message.RecipientDeleted;
    }

或者,也可以将其内联以保持闭包的使用:

    private Expression<Func<Message, bool>> UserSelector(string username, bool sent)
    {
        Func<Message, bool> innerFunc = message =>
        {
            if (sent)
            {
                return string.Equals(message.FromUser.Username, username, StringComparison.InvariantCultureIgnoreCase) &&
                        !message.SenderDeleted;
            }
            return string.Equals(message.ToUser.Username, username, StringComparison.InvariantCultureIgnoreCase) &&
                    !message.RecipientDeleted;
        };
        return message => innerFunc(message);
    }

(编辑为使用 string.EqualsStringComparison.InvariantCultureIgnoreCase 处理具有不同文化设置的奇数边缘情况。)

【讨论】:

  • 我喜欢两个函数的想法,在集合上做两个.Where链,每个表达式一个。这是非常可读的。
  • 看看我的编辑以实现你的答案:)
  • @Davy8 我删除了我的答案,但我认为 Jeffrey L Whitledge 对are-string-equals-and-operator-really-same 的回答中的link 可能会将 InvariantCultureIgnoreCase 位置于上下文中
  • 信不信由你,我实际上不得不从我的表达式中删除 string.Equals。它不喜欢它。我不得不回到.ToLower()。该应用程序会编译,但在运行时会显示“为方法 string.Equals 提供的参数数量无效”。我对参数进行了三次检查,甚至对其进行了调试,并且它们都通过了。只是不喜欢的表情而已。如果我在 main 方法中完成它,它工作正常,只是不在表达式中。
  • 好的,我得到了 string.Equals 的工作,但只能通过调用它的实例等效。我打电话给str.Equals(str, ignoreCase) 而不是string.Equals(str, str, ignoreCase),这在表达式中似乎可以正常工作。似乎调用静态字符串类是问题。
【解决方案2】:

只要您将为此问题编写的描述作为评论写下,它应该可以帮助任何试图阅读/理解它的人正确吗?

【讨论】:

  • 好吧,我只是看看你们能想出什么。我经常惊讶于人们做事比我想象的要容易得多。
【解决方案3】:

你有 Resharper 吗?很多时候我会写这样的东西作为 foreach 循环,看看 Resharper 是否可以重构它。不过,我认为可读性是最重要的,尽管您应该评估您提出的任何解决方案的性能。

【讨论】:

  • 有人想澄清他们为什么不赞成这个吗?只是对它有什么问题感兴趣。
  • 回复太笼统,无法作为原始问题的特定答案。
  • 答案是隐含的。不要写复杂的 1 liner LINQ 语句,除非你觉得性能比可读性更重要,通常情况并非如此。
  • @Codism,我明白了。我同意这一点。可能是评论而不是回答。
猜你喜欢
  • 2011-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-01
  • 2021-04-01
  • 2020-04-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多