C#linqorderby和其他带有foreach的语句,是否存在性能差异

本文关键字:是否 存在 性能 语句 其他 foreach C#linqorderby | 更新日期: 2023-09-27 18:27:03

我正在对实体框架、集合和其他方面进行大量编程,并根据操作确定条件和顺序。我问自己这个问题已经有一段时间了,但一直没能弄清楚。

假设我有以下两段代码;

示例1:

// An unsorted string array.
string[] letters = { "d", "c", "a", "b" };
// Use LINQ query syntax to sort the array alphabetically.
var sorted = from letter in letters
         orderby letter
         select letter;
// Loop with the foreach keyword.
foreach (string value in sorted)
{
    Console.WriteLine(value);
}

示例2:

// An unsorted string array.
string[] letters = { "d", "c", "a", "b" };
// Loop with the foreach keyword.
foreach (string val in letters.OrderBy(l => l))
{
    console.writeline(val)
}

第一个例子首先对结果集执行order by,然后在集合上迭代,其中第二个例子在我们要迭代它的时候具有order by。。性能上有什么不同(如果有的话)?这两种方法中的一种比另一种好吗?和where条件(带连接的简单条件和复杂条件)一样,有什么显著的区别吗?

C#linqorderby和其他带有foreach的语句,是否存在性能差异

两者之间没有区别。。

代码-

static void Main(string[] args)
{
    string[] letters = { "d", "c", "a", "b" };
    // Use LINQ query syntax to sort the array alphabetically.
    var sorted = from letter in letters
                    orderby letter
                    select letter;
    // Loop with the foreach keyword.
    foreach (string value in sorted)
    {
        Console.WriteLine(value);
    }
    foreach (string val in letters.OrderBy(letter => letter))
    {
        Console.WriteLine(val);
    }
}

生成的代码-

private static void Main(string[] args)
{
  string[] strArray1 = new string[4]
  {
    "d",
    "c",
    "a",
    "b"
  };
  string[] strArray2 = strArray1;
  if (Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate2 == null)
  {
    // ISSUE: method pointer
    Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate2 = new Func<string, string>((object) null, __methodptr('u003CMain'u003Eb__0));
  }
  Func<string, string> keySelector1 = Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate2;
  foreach (string str in (IEnumerable<string>) Enumerable.OrderBy<string, string>((IEnumerable<string>) strArray2, keySelector1))
    Console.WriteLine(str);
  string[] strArray3 = strArray1;
  if (Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate3 == null)
  {
    // ISSUE: method pointer
    Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate3 = new Func<string, string>((object) null, __methodptr('u003CMain'u003Eb__1));
  }
  Func<string, string> keySelector2 = Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate3;
  foreach (string str in (IEnumerable<string>) Enumerable.OrderBy<string, string>((IEnumerable<string>) strArray3, keySelector2))
    Console.WriteLine(str);
}
[CompilerGenerated]
private static string 'u003CMain'u003Eb__0(string letter)
{
  return letter;
}
[CompilerGenerated]
private static string 'u003CMain'u003Eb__1(string letter)
{
  return letter;
}

编辑:这是一个有趣的变体,我很想尝试一下。。在上述情况下,对于查询表达式,编译器足够聪明,可以优化Select。。但是,如果在第二个变体中,我们使用显式Select。这并不奇怪,但编译器并没有优化掉显式的.Select(而查询表达式中的显式Select是编译器的要求)。

代码-

    foreach (string val in letters.OrderBy(letter => letter).Select(letter => letter))
    {
        Console.WriteLine(val);
    }

生成的代码-

  string[] strArray4 = strArray1;
  if (Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate6 == null)
  {
    // ISSUE: method pointer
    Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate6 = new Func<string, string>((object) null, __methodptr('u003CMain'u003Eb__2));
  }
  Func<string, string> keySelector3 = Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate6;
  IOrderedEnumerable<string> orderedEnumerable = Enumerable.OrderBy<string, string>((IEnumerable<string>) strArray4, keySelector3);
  if (Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate7 == null)
  {
    // ISSUE: method pointer
    Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate7 = new Func<string, string>((object) null, __methodptr('u003CMain'u003Eb__3));
  }
  Func<string, string> selector = Program.CS'u0024'u003C'u003E9__CachedAnonymousMethodDelegate7;
  foreach (string str in Enumerable.Select<string, string>((IEnumerable<string>) orderedEnumerable, selector))
    Console.WriteLine(str);

您的第一个查询相当于

... = letters.OrderBy(letter => letter).Select(letter => letter);

您的第二个查询是

... in letter.OrderBy(l => l)) {

两个查询几乎相同,只有第一个查询有一个额外的.Select(...)调用,您可以在其中选择给定的输入,所以这是毫无意义的。C#编译器可能会删除调用,但您必须查看生成的IL才能知道这一点。

此外,您的第一个语句不执行查询。.OrderBy(...)和大多数linq语句都是查询定义。这意味着您对.OrderBy(...)的调用实际上并没有执行,这是一个直到您迭代结果才会得到回答的问题。因此,在这两个版本中,当foreach (... in <collection>)访问它要迭代的集合时,都会执行查询。

结论:性能差异会很小,以至于你必须非常努力地发现任何真正的差异。当然,这是我谦虚的猜测。

我能看到的唯一区别是,在第一个代码中,Iteratorforeach之前创建(未执行),并且您将其存储在本地变量中。在第二个代码中,foreach直接调用迭代器上的GetEnumarator方法,而不是本地变量。但这当然不会对性能产生任何影响。除此之外,由于可读性,我更喜欢第二个。