Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/273.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 迭代集合时IEnumerable vs List_C# - Fatal编程技术网

C# 迭代集合时IEnumerable vs List

C# 迭代集合时IEnumerable vs List,c#,C#,我的问题基本上是什么是好的编程实践。在IEnumerable的情况下,每个项的计算时间与ToList的情况相同,整个集合在开始for循环之前进行迭代。 根据下面的代码,应该使用哪个函数(GetBool1 vs GetBool2)以及为什么 public class TestListAndEnumerable1 { public static void Test() { GetBool1(); GetBool2(); Console

我的问题基本上是什么是好的编程实践。在IEnumerable的情况下,每个项的计算时间与ToList的情况相同,整个集合在开始for循环之前进行迭代。 根据下面的代码,应该使用哪个函数(GetBool1 vs GetBool2)以及为什么

public class TestListAndEnumerable1
{
    public static void Test()
    {
        GetBool1();
        GetBool2();

        Console.ReadLine();
    }

    private static void GetBool1()
    {
        var list = new List<int> {0,1,2,3,4,5,6,7,8,9};

        foreach (var item in list.Where(PrintAndEvaluate))
        {
            Thread.Sleep(1000);
        }
    }

    private static bool PrintAndEvaluate(int x)
    {
        Console.WriteLine("Hi from " + x);
        return x%2==0;
    }

    private static void GetBool2()
    {
        List<int> list = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        foreach (var item in list.Where(PrintAndEvaluate).ToList())
        {
            Thread.Sleep(1000);
        }
    }
}
公共类TestListAndEnumerable1
{
公共静态无效测试()
{
GetBool1();
GetBool2();
Console.ReadLine();
}
私有静态void GetBool1()
{
var list=新列表{0,1,2,3,4,5,6,7,8,9};
foreach(列表中的var项目,其中(打印和评估))
{
睡眠(1000);
}
}
私有静态布尔打印和评估(int x)
{
Console.WriteLine(“Hi from”+x);
返回x%2==0;
}
私有静态void GetBool2()
{
列表=新列表{0,1,2,3,4,5,6,7,8,9};
foreach(list.Where(printandeevaluate.ToList()中的变量项)
{
睡眠(1000);
}
}
}

这两个循环的性质不同。在第一种情况下,在迭代和计算每个项时,控制台将被写入,并且每个Console.Write之间将发生休眠

在第二种情况下,控制台写入操作也将被评估,但这些评估都将在休眠之前进行-只有在所有printandeevaluate调用完成时才会进行

第二种情况是两次枚举列表的成员,同时分配和分割内存

如果你的问题是“哪种方法最有效”,那么答案就是第一个例子,但是如果你想知道“还有其他更有效的方法吗”,那么就使用一个循环

  for(int counter = 0 ; counter <= list.Count; counter ++)
    {
        if(PrintAndEvaluate(list[counter]))
        {
           Thread.Sleep(1000);
        }
    }

应使用(int counter=0;counter
GetBool1
for

这两种方法之间唯一的区别是存在
ToList()
调用,对吗

让我们先看一下
ToList
调用的意义:

IEnumerable
创建
列表

这意味着在调用
ToList
时将创建一个新列表。您可能知道,创建一个新列表需要时间和内存


另一方面,
GetBool1
没有
ToList
调用,因此它不需要花费太多的时间来执行。

GetBool1是更好的选择。对于选项2,即使您将IEnumerable转换为List,当您再次调用foreach时,它也会调用GetEnumerator。但是差别非常小。我对您做了一点修改用于输出执行时间的r代码:

 public static void Test()
        {
            var list = new List<int>();
            for (int i = 0; i < 10000; i++)
            {
                list.Add(i);
            }

            GetBool1(list);
            GetBool2(list);
            GetBool3(list);
            Console.ReadLine();
        }

        private static void GetBool1(List<int> list)
        {
            System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();

            watcher.Start();
            foreach (var item in list.Where(PrintAndEvaluate))
            {
                Thread.Sleep(1);
            }
            watcher.Stop();
            Console.WriteLine("GetBool1 - {0}", watcher.ElapsedMilliseconds);
        }

        private static bool PrintAndEvaluate(int x)
        {
            return x % 2 == 0;
        }

        private static void GetBool2(List<int> list)
        {
            System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();

            watcher.Start();
            foreach (var item in list.Where(PrintAndEvaluate).ToList())
            {
                Thread.Sleep(1);
            }
            watcher.Stop();
            Console.WriteLine("GetBool2 - {0}", watcher.ElapsedMilliseconds);
        }
公共静态无效测试()
{
var list=新列表();
对于(int i=0;i<10000;i++)
{
列表.添加(i);
}
GetBool1(列表);
GetBool2(列表);
GetBool3(列表);
Console.ReadLine();
}
私有静态void GetBool1(列表)
{
System.Diagnostics.Stopwatch watcher=新的System.Diagnostics.Stopwatch();
watcher.Start();
foreach(列表中的var项目,其中(打印和评估))
{
睡眠(1);
}
watcher.Stop();
WriteLine(“GetBool1-{0}”,watcher.elapsedmillesons);
}
私有静态布尔打印和评估(int x)
{
返回x%2==0;
}
私有静态void GetBool2(列表)
{
System.Diagnostics.Stopwatch watcher=新的System.Diagnostics.Stopwatch();
watcher.Start();
foreach(list.Where(printandeevaluate.ToList()中的变量项)
{
睡眠(1);
}
watcher.Stop();
WriteLine(“GetBool2-{0}”,watcher.elapsedmillesons);
}
输出为:

GetBool1():创建一个枚举器并循环,返回匹配结果并运行代码块。GetBool2():创建一个枚举器返回匹配结果,将匹配结果复制到新列表(分配更多内存!如果列表是值类型列表,可能会有很多内存!),创建枚举数并循环,然后运行代码块。您认为哪一个更好?有关差异的更多信息,请查看…?比较不完全相同的方法似乎毫无意义。此外,对单个迭代进行基准测试似乎是可疑的,特别是当您甚至没有首先对方法进行JIT时。幸运的是,您提到了“但这些评估都会在睡觉前进行。”斯威普的答案在这里缺失的是,即使这个问题看起来像“时间和记忆差异”这样简单,实际上还有更多的问题。重要的是要记住,如果你不调用
ToList
或任何其他“物化”"因此,对基础集合的任何更改都将反映在各个元素求值之间。因此,如果在睡眠之间向集合添加新项,则这些添加将反映出来……如果调用ToList,则情况并非如此。在这种情况下,无论原始集合发生了什么变化,这些更改都将反映出来不被反映。这可能是期望的,也可能是不期望的,这取决于具体情况,因此在某些情况下,调用ToList是可取的,而在其他情况下则不是。一个字等于一百个字,这肯定不仅仅是关于时间和内存。ToList的存在实际上改变了代码执行的顺序。