【问题标题】:When should I use LINQ for C#? [closed]什么时候应该为 C# 使用 LINQ? [关闭]
【发布时间】:2010-02-02 18:53:58
【问题描述】:

我正在学习 C#,我发现 LINQ 非常有趣。然而令我惊讶的是,我想不出使用 LINQ 会有巨大帮助的场景,因为在代码中复制 LINQ 功能真的不是那么难。

您可能想分享任何个人经验/建议吗?

谢谢!

【问题讨论】:

  • 以“主观和争论”为由投票结束。这太白痴了!这是一个关于 Linq 独特价值的简单问题。 (另一方面,这肯定是关于 SO 的一个大量重复的问题,因此可能因此被关闭。)
  • 我愿意打赌他们是“维基或关闭”投票。这是主观的,所以它可能应该是一个 wiki 问题。
  • 谢谢你。我只是好奇,我不明白为什么人们认为这是一个主观原因。我不知道问我如何在功能中编码是主观的。这几天的社区。啧啧。
  • “我正在学习 C#,我发现 foreach 非常有趣。但是让我感到困惑的是,我想不出使用 foreach 会有很大帮助的场景,因为在代码中复制foreach 功能真的不是那么难。”
  • Google 搜索显示 LINQ 的结果数量如此荒谬,所有这些结果都包含大量示例。 SO 有 90 页有关 LINQ 的问题。我看不出这如何为大量现有信息添加任何内容。应该关闭或 wiki。

标签: c# .net linq


【解决方案1】:

我发现我几乎在任何时候都在使用 LINQ,而我之前会编写一个循环来填充容器。我使用 LINQ to SQL 作为我的 ORM,并在其他地方使用大量 LINQ。

这是我为 Active Directory 帮助程序类编写的一个小 sn-p,它可以确定特定用户是否是特定组。请注意使用 Any() 方法遍历用户的授权组,直到找到具有匹配 SID 的授权组。比替代方案更简洁的代码。

private bool IsInGroup( GroupPrincipal group, UserPrincipal user )
{
    if (group == null || group.Sid == null)
    {
        return false;
    }
    return user.GetAuthorizationGroups()
               .Any( g => g.Sid != null && g.Sid.CompareTo( group.Sid ) == 0 );
}

替代方案:

private bool IsInGroup( GroupPrincipal group, UserPrincipal user )
{
    if (group == null || group.Sid == null)
    {
        return false;
    }
    bool inGroup = false;
    foreach (var g in user.GetAuthorizationGroups())
    {
         if ( g => g.Sid != null && g.Sid.CompareTo( group.Sid ) == 0 )
         {
            inGroup = true;
            break;
         }
    }
    return inGroup;
}

private bool IsInGroup( GroupPrincipal group, UserPrincipal user )
{
    if (group == null || group.Sid == null)
    {
        return false;
    }

    foreach (var g in user.GetAuthorizationGroups())
    {
         if ( g => g.Sid != null && g.Sid.CompareTo( group.Sid ) == 0 )
         {
            return true;
         }
    }
    return false;
}

这是一个 sn-p,它对存储库进行搜索、订购并将前 10 个匹配的业务对象转换为特定于视图的模型(Distance 是匹配模型的唯一 id 与 uniqueID 的 Levenshtein 编辑距离参数)。

model.Results = this.Repository.FindGuestByUniqueID( uniqueID, withExpired )
                               .OrderBy( g => g.Distance )
                               .Take( 10 )
                               .ToList()
                               .Select( g => new GuestGridModel( g ) );

【讨论】:

  • 有趣,详细说明循环/填充容器?一些代码示例?
  • 严格来说,这不是使用 LINQ,而是使用 LINQ 语法转换成的扩展方法。我只是发现流畅的风格对我来说更容易阅读——在使用 jQuery 来回切换时认知失调更少。您当然也可以使用 LINQ 语法来表示上述所有内容。
  • 呃,如果它在 System.Linq 命名空间中,我认为它算作使用 LINQ,无论您使用哪种语法调用它。
  • 为什么在第二个示例中找到用户后还要继续迭代用户?你可以return true;
  • @ldan K - 是的,我可以简单地返回 true,但我更喜欢重用函数中的最终退出点。这只是个人偏好(而且我并不总是一致的,请注意,如果组或用户为空,我会提前返回)。不过,我不会继续迭代,请注意我在将 inGroup 设置为 true 后立即中断了循环。不过,我将提供替代方案,它比使用 LINQ 更简单,但仍然不那么紧凑。
【解决方案2】:

这取决于您的意思是哪种 linq。

是 linq-to-sql 吗?在这种情况下,它是一个具有与使用任何其他 orm 相同的好处的 orm。我用的不多,真的不能多说。

它是 linq-to-objects 吗?在这种情况下,您实际上是在谈论其他东西的集合:扩展方法、惰性迭代器和查询理解语法。这是对函数式编程世界的介绍。我对查询理解语法没有太多用处,但对于其余的,我最好用一个例子来演示。

假设您要逐行读取文件。对于每一行,您要检查它是否满足某些条件,将这些行的一部分转换为整数,并对同样在特定范围内的前 10 个整数求和。这是您可以执行此操作的旧方法:

int SumXValues(string filename)
{
    string line;
    int sum = 0;
    int count = 0;
    using (var rdr = new StreamReader(filename))
    {

        while ( (line = rdr.ReadLine()) != null && count < 10)
        {
           int val;
           if (int.TryParse(line.Substring(3,3))
           {
               if (val > 4 && val < 25)
               {
                    sum += val;
                    count ++;
               }
            }
        }
    }
    return sum;
}

这是新方法:

IEnumerable<string> ReadLines(string filename)
{
    string line;
    using (var rdr = new StreamReader(filename))
        while ( (line = rdr.ReadLine()) != null)
           yield return line;
}

int SumXValues(string filename)
{
    return ReadLines(filename)
               .Select(l => l.Substring(3,3))
               .Where(l => int.TryParse(l))
               .Select(i => int.Parse(i))
               .Where(i => i > 4 && i < 16)
               .Take(10)
               .Sum(i => i);
}

请注意,新代码实际上更短。但为什么它也更好?有(至少)4 个原因:

  • 希望 readlines 函数的可重用性是显而易见的。您也可以考虑更多因素,但重点是展示这种风格如何帮助您重用更多代码。
  • 它的扩展性更好。注意最后一个函数中的所有链式函数调用。您知道该代码将迭代文件中的行多少次吗? 恰好一次! 事实上,即使在获取前 10 个项目后它会停止从文件中读取,也不会如此。如果您将其更改为返回一个可枚举,然后将其与其他扩展方法一起在其他地方使用? 仍然只有一次!这使您可以在运行时构建、混合和重新混合查询结果,而无需对列表进行昂贵的额外传递。
  • 它更易于维护。如果标准发生变化,很容易找出您关心的确切“规则”(如果您愿意的话)并仅修改该部分。
  • 它更具可读性。这使您可以根据代码正在做什么而不是如何执行来表达代码。

【讨论】:

  • +!很好的例子,很好的理由
【解决方案3】:

当我有一些对象的集合并且我对满足特定条件的项目感兴趣时,我发现 LINQ 很有用。简单的示例是在圆形形状集合中搜索所有形状。

var circles =
        from s in allShapes
        where s.Type == ShapeTypes.Circle
        select s;

我承认我本可以编写一个包含一些代码的循环,但我发现这段代码更短、更容易编写、更易于阅读和理解。

LINQ 也可以与数据库一起使用,但实际上我发现我不怎么做。我猜是他/她自己的。

【讨论】:

    【解决方案4】:

    Essential LINQ 这本书对 LINQ 的好处进行了精彩的一段总结:

    LINQ 不仅仅是添加新的 语言的特点。它 引入了一种贬义的风格 编程为 C# 语言。这 声明式编程模型允许 开发人员编写代码 简洁地捕捉到他们的意图, 不强迫他们担心 事件发生的顺序, 或它们的精确实施。它 允许开发人员说明他们的 想做,而不是怎么做 完成。

    【讨论】:

      【解决方案5】:

      您是对的,在常规 C# 中复制这些功能很少非常困难。这就是他们所说的语法糖。这只是为了方便。你知道自动属性吗?那些允许您编写public class User { public int Id { get; set; } } 的代码非常很容易在“常规”C# 代码中复制。即便如此,自动属性仍然很棒;)

      过滤和排序几乎是 LINQ 的核心。假设您有一个名为 users 的用户列表,并且您想找到今天登录的用户,并按照最近登录的顺序显示它们。在 LINQ 之前,您可能会执行类似的操作创建一个新列表,您根据原始列表填充它,通过迭代它,添加符合条件的内容,然后实现某种IComparable。现在你可以这样做了:

      users = users.Where(u => u.LastLoggedIn.Date = DateTime.Today)
                   .OrderBy(u => u.LastLoggedIn).ToList();
      

      方便 =)

      【讨论】:

      • 为什么还要调用 .ToList()? 10 次中有 9 次不需要它,它会影响你的表现。
      • 我同意这一点,但我的示例假设用户列表,如前面的文本。授予,我可以很容易地改变我的例子 =)
      【解决方案6】:

      我经常使用 LINQ to DataSet 来处理 DataTables。 DataTables 是通用数据存储,我经常在其中存储值,例如从加载的 CSV 文件。使用 LINQ 来查询或连接数据比使用 for 循环“暴力破解”更具可读性和舒适性。

      【讨论】:

        【解决方案7】:

        我想把这个问题转过来:你能告诉我们如果没有它你将如何模拟 Linq 功能吗?我在想一个不是巨大帮助的案例时遇到了麻烦。

        例如,我最近看到这样的事情:

        foreach (var person in people.OrderBy(p => p.Company)
                                     .ThenBy(p => p.LastName)
                                     .ThenBy(p => p.FirstName)) {
            ...
        }
        

        我猜它可能使用了Arrays.Sort,创建了一个以正确顺序检查字段的委托(从书面倒过来,对吗?),然后忍受它只能在数组上工作的事实。这似乎会更长、更难维护、更不灵活。

        【讨论】:

          【解决方案8】:

          是的,您可以使用替代代码轻松地使用 LINQ to Objects,这并不难。我倾向于喜欢 lambda 表达式的语义,它确实将几行代码包装成一行。但是你可以用你自己的代码做很多操作,除了一些更大的操作(联合、相交等)用 LINQ 更容易完成。

          LINQ 有其他风格; LINQ to XML 使得处理 XML 数据变得非常好。我真的比以前可用的对象更喜欢它。

          LINQ to SQL 和 ADO.NET Entity Framework(带有 LINQ to Entities)是一个对象关系映射器,可以映射到您的数据库表,并且像存储过程和 ADO.NET 数据集一样,所以这是一个非常好的选择比弱数据集/数据表,我也喜欢强类型数据集/表。

          HTH。

          【讨论】:

          • 因为相交/联合提及而投票。这也是我发现 LINQ 最大的好处 :) 除此之外,它通常比 foreach 循环更好地记录意图。
          【解决方案9】:

          看看 Jon Skeet 为 Linq 问题提供的众多答案中的任何一个,您就会发现 Linq 真正的多功能性和实用性。

          https://stackoverflow.com/search?q=user:22656+[linq]

          【讨论】:

            【解决方案10】:

            如果您还没有,请查看 ReSharper。它有很多关于“...转换为 LINQ 语法”的提示,因此它通常可以向您展示您在学习时没有考虑的一两件事。 :)

            【讨论】:

              【解决方案11】:

              有人提到 LINQ 是一种声明式编程风格。我只是想对此进行扩展。

              我使用 LINQ 的一种方法是编写测试 oracle 代码。测试代码要简单并且尽可能接近“明显正确”是非常重要的,否则它最终会出现与它应该测试的代码一样多的错误。对于我现在正在测试的一个特定功能,我写了一个集合推导的小列表,准确地描述了我期望该功能如何工作。多亏了 LINQ,将这些理解转换为代码变得非常容易:

              A = all items
              B = [x in A: x.Type = selectedtype, x.Source = "sourceA"]
              C = [x in A: x.Source = "sourceB"]
              D = B union C
              

              在代码中:

              IEnumerable<MyClass> SetB(IEnumerable<MyClass> allItems, MyType type)
              {
                var result = from item in allItems
                             where item.Type == type && item.Source == "sourceA"
                             select item;
                return result;
              }
              
              IEnumerable<MyClass> SetC(IEnumerable<MyClass> allItems)
              {
                var result = from item in allItems
                             where item.Source == "sourceB"
                             select item;
                return result;
              }
              
              IEnumerable<MyClass> SetD(IEnumerable<MyClass> allItems, MyType type)
              {
                var setB = SetB(allItems, type);
                var setC = SetC(allItems);
                return setB.Union(setC);
              }
              

              虽然仍然比数学表达式更冗长,但调用“明显正确”比命令式代码更简单、更容易。 LINQ 代码是声明性的,就像数学是声明性的一样。更少的翻译,更接近规范。如果使用得当,LINQ 几乎是一种“按我的意思行事”的语言。

              请注意,我不会以这种方式编写实际代码。性能不是对测试代码的要求,而是对真实代码的要求,所以真实代码还是需要是SQL存储过程。

              【讨论】:

                【解决方案12】:

                对我来说,我几乎只用它来访问数据库。我几乎从不查询 XML 或列表。

                【讨论】:

                  【解决方案13】:

                  我发现在将数据绑定到网格以用于只读目的之前转换/“投影”数据很有用。

                  【讨论】:

                    【解决方案14】:

                    LINQ 语言功能集在 C# 2.0 代码中是不可复制的,没有扩展方法、lambda 表达式,甚至没有查询运算符。 LINQ 的全部意义在于您有一些集成版本的查询,您可以在其中使用编译器对它们进行完整性检查。另一个关键点是统一不同数据源的视图,无论是数据库还是内存中的集合。你可以不使用 LINQ,就像你可以不使用 Assembler 中的语言和代码的任何其他特性一样,但是有一些难以监督的明显优势;)

                    【讨论】:

                      【解决方案15】:

                      再补充一点是一种总结。您可以从上面看到,它用于方便和简洁,可以与集合、SQL、XML(以及任何其他关心实现 LINQ 提供程序的东西)一起使用,但最重要的是,您只需学习一次 LINQ 语法就可以了延伸到所有这些有用的技术领域。

                      【讨论】:

                        猜你喜欢
                        • 2017-09-11
                        • 1970-01-01
                        • 2012-12-23
                        • 2021-07-13
                        • 2016-01-03
                        • 2022-01-02
                        • 2010-10-18
                        • 2011-07-19
                        • 1970-01-01
                        相关资源
                        最近更新 更多