【问题标题】:Enumerable.Range - When does it make sense to use?Enumerable.Range - 什么时候使用有意义?
【发布时间】:2014-06-20 06:47:58
【问题描述】:

在编程时,几乎本能地决定何时使用 for 循环或 foreach,但选择使用 Enumerable.Range 的决定因素或问题空间是什么?

For 循环在我们想要迭代一定次数(通过简单的数据类型)来计算/执行重复性任务时选择。

For Each 类似,但在我们想要遍历复杂对象列表以计算/执行重复性任务时选择。

再次,使用 Enumerable.Range 的决定因素是什么?

IEnumerable<int> squares = Enumerable.Range(4, 3).Select(x => x * x);

【问题讨论】:

  • 为了加入另一个序列?
  • 为单元测试生成数据?
  • 您的 squares 示例已经显示了它,您不能在 linq 表达式中使用 for 循环(除非它首先用于填充集合)。所以我会说这是使用 Enumerable.Range 的明确案例。
  • @Me.Name 非常感谢。但这只是一种情况,我可以使用 for 循环构建结果来执行相同的代码。我真的只是想弄清楚上面的声明(使用 For/Foreach),这告诉我,现在是使用 Enumerable.Range 的时候了。
  • @DaneBalia:嗯,你可以用 LINQ 做的所有事情,你也可以用循环来做(这通常是 LINQ 方法在底层实现的方式)。它们是做同样事情的两种不同方式,一种更“功能性”(LINQ),另一种更“必要”,因此,Enumerable.Range 是完成 LINQ 库所必需的(否则你将如何实现 @987654323 @循环?)

标签: c# .net linq


【解决方案1】:

foreach 是关于迭代现有的集合/集合。

Enumerable.Range 用于生成一个集合/集合。通常,您不会想要编写一个 for 循环来生成一个集合,如果它可以由 Enumerable.Range 生成 - 您只是在编写更长的样板代码并且需要您分配某种存储(例如 List&lt;int&gt;) 先填充。

【讨论】:

    【解决方案2】:

    如前所述,Enumerable.Range 不是针对循环,而是创建范围。这使得 Linq 中的一个衬垫成为可能,而无需创建子集。 该功能的另一个优势是,您甚至可以在 sub 语句中生成子范围,这对于 for 和 lambda 而言并非总是可行的,因为在 lambda 中无法实现 yield。

    例如,SelectMany 也可以使用 Enumerable.Range。 测试集合:

    class House
    {
        public string Name { get; set; }
        public int Rooms;
    }
    
        var houses = new List<House>
        {
            new House{Name = "Condo", Rooms = 3},
            new House{Name = "Villa", Rooms = 10}
        };
    

    当然,这个例子本身并没有多大价值,但是为了获得所有房间,Linq 中的实现是:

       var roomsLinq = houses.SelectMany(h => Enumerable.Range(1, h.Rooms).Select(i => h.Name + ", room " + i));
    

    如果使用迭代,则需要 2 次迭代:

       var roomsIterate = new List<string>();
        foreach (var h in houses)
        {
            for (int i = 1; i < h.Rooms + 1; i++)
            {
                roomsIterate.Add(h.Name + ", room " + i);
            }
        }
    

    您仍然可以说,第二个代码更具可读性,但这归结为是否使用 Linq。


    因此,更进一步,我们需要所有房间的IEnumerable&lt;IEnumerable&lt;string&gt;&gt;(每个房子的房间的可枚举字符串)。

    林克:

    listrooms = houses.Select(h => Enumerable.Range(1, h.Rooms).Select(i => h.Name + ", room " + i));
    

    但是现在,我们在使用迭代时需要 2 个集合:

        var list = new List<IEnumerable<string>>();
        foreach (var h in houses)
        {
            var rooms = new List<string>();
            for (int i = 1; i < h.Rooms + 1; i++)
            {
                rooms.Add(h.Name + ", room " + i);
            }
            list.Add(rooms);
        }
    

    另一个场景,我认为 Linqs 和 lambdas 的一大优点是您可以将它们用作参数(例如,用于注入目的),这可以通过 Enumerable 以更简单的方式实现。范围。

    例如,你有一个函数,它带有一个参数 roomgenerator

    static IEnumerable<Furniture> CreateFurniture(Func<House,IEnumerable<string>> roomgenerator){
       //some house fetching code on which the roomgenerator is used, but only the first 4 rooms are used, so not the entire collection is used.
    }
    

    上面的房间迭代可以用 Enumerable.Range 返回,但是通过迭代,必须首先创建房间的子集合,或者生成结果的单独函数。 子集合有一个很大的缺点,即它总是被完全填充,即使枚举中只需要一项。单独的方法通常是多余的,因为它只需要使用单个参数,因此 Enumerable.Range 可以节省一天的时间。

    【讨论】:

    • 刚刚想到另一个场景:你可以在 Enumerable.Range 上使用AsParallel :)
    【解决方案3】:

    Enumerable.Range() 是一个生成器,即它是一种生成n 某种项目的简单而强大的方法。

    需要一个包含某个类的随机实例数的集合吗?没问题:

    Enumerable.Range(1,_random.Next())
        .Select(_ => new SomeClass
        {
            // Properties
        });
    

    【讨论】:

      【解决方案4】:

      你的例子已经足够好了。

      我可以用 for 循环构建结果来执行相同的代码

      基本上有两种方法可以使用循环构建它:

      // building a collection for the numbers first
      List<int> numbers = new List();
      for (int i = 4; i < 7; i++)
          numbers.Add(i);
      
      IEnumerable<int> squares = numbers.Select(x => x * x);
      
      // or building the result directly
      List<int> squares = new List();
      for (int i = 4; i < 7; i++)
          numbers.Add(i * i);
      

      这两种解决方案的问题是您实际上需要创建一个要添加到的集合。所以你在某处有一个数字集合。

      但是看看Enumerable.Range 的解决方案:Enumerable.Range 是一个生成器,它返回一个IEnumerable,就像所有其他Linq 方法一样,只在它们被迭代时创建。所以永远不存在包含所有数字的集合。

      【讨论】:

        【解决方案5】:

        您可以将Enumerable.Range 视为检索集合子集的工具。

        如果集合的内容是值类型,你还要为它们创建一个新的实例。

        是的,您可以使用for 循环,通过设置正确的迭代边界来做到这一点,但是这样想,几乎所有LINQ 都可以用其他方式实现(最后它只是一个库)。

        但它的语法简洁明了,这就是它被广泛使用的原因。

        【讨论】:

          【解决方案6】:

          回答

          for 更快

          Enumerable.Range 更具可读性/更简洁

          如果您正在处理大量数组,请使用 for 循环。如果您处理较小的输入,Enumerable.Range 是首选 - 选择您的权衡。

          性能测试

          public static void Main(string[] args)
          {
              var stopwatch = new Stopwatch();
              var size = 1000000;
              int[] arrRange;
              
              stopwatch.Start();
              arrRange = Enumerable.Range(0, size).ToArray();
              stopwatch.Stop();
              P(stopwatch, "Enumerable.Range");
          
              stopwatch.Reset();
              var arrFor = new int[size];
              stopwatch.Start();
              for (var i = 0; i < size; i++)
                  arrFor[i] = i;
              stopwatch.Stop();
              P(stopwatch, "For");
          
              var arrWhile = new int[size];
              var iw = 0;
              stopwatch.Reset();
              stopwatch.Start();
              while (iw < size)
              {
                  arrWhile[iw] = iw;
                  iw++;
              }
              stopwatch.Stop();
              P(stopwatch, "while");
          
              stopwatch.Reset();
              stopwatch.Start();
              var arrRepeat = Enumerable.Repeat(1, size);
              stopwatch.Stop();
              P(stopwatch, "Enumerable.Repeat");
          }
          
          public static void P(Stopwatch s, string type)
          {
              Console.WriteLine($"{type}: {s.Elapsed}");
          }
          

          这是这个程序的输出(在 .NET 6.0 中)

          Enumerable.Range: 00:00:00.0070907
          For: 00:00:00.0036731
          while: 00:00:00.0036175
          Enumerable.Repeat: 00:00:00.0003927
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-01-26
            • 2013-10-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-09-16
            • 2011-01-09
            相关资源
            最近更新 更多