在编程中,使用for循环或foreach循环几乎是本能的选择,但是在什么情况下会选择使用Enumerable.Range呢?
当我们想要迭代一组已知次数的数据类型以计算/执行重复任务时,选择for循环。
当我们想要迭代一组复杂对象列表以计算/执行重复任务时,选择foreach循环。
那么,使用Enumerable.Range的决定因素是什么呢?
IEnumerable<int> squares = Enumerable.Range(4, 3).Select(x => x * x);
foreach
是用于在现有的组合/集合上进行迭代。
Enumerable.Range
用于生成一个集合。如果可以通过Enumerable.Range
来生成一个集合,通常不需要编写 for
循环- 这只会让你编写更长的样板代码,并需要先分配一些存储(例如 List<int>
)进行填充。
正如提到的那样,Enumerable.Range并不是专门用于循环,而是用于创建范围。这使得在Linq中可以轻松完成一行代码,而无需创建子集。
另一个优点是,您甚至可以在子语句中生成子范围,这在使用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<IEnumerable<string>>
(每个房屋的字符串枚举房间)。
Linq:
listrooms = houses.Select(h => Enumerable.Range(1, h.Rooms).Select(i => h.Name + ", room " + i));
但是现在,在迭代时我们需要使用两个集合:
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);
}
另一个场景,我认为 Linq 和 lambda 的伟大之处之一在于,您可以将它们用作参数(例如,用于注入目的),这在使用 Enumerable.Range 更容易实现。
例如,您有一个函数,需要一个名为 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可以解决问题。
AsParallel
:) - Me.NameEnumerable.Range()
是一个生成器,也就是它是一种简单而强大的方式来生成某种类型的n
个项。
需要一个包含某个类的随机数量实例的集合吗?没问题:
Enumerable.Range(1,_random.Next())
.Select(_ => new SomeClass
{
// Properties
});
你的示例已经足够好了。
我可以使用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
是一个生成器,它返回一个`IEnumerable`,就像所有其他Linq方法一样,并且只有在迭代时才会创建这些内容。因此,永远不存在包含所有数字的集合。Enumerable.Range
视为检索集合子集的工具。for
循环来做到这一点,通过正确设置迭代范围,但是以这种方式思考,几乎所有的LINQ
都可以用其他方式实现(最终它只是一个库)。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}");
}
Enumerable.Range: 00:00:00.0070907
For: 00:00:00.0036731
while: 00:00:00.0036175
Enumerable.Repeat: 00:00:00.0003927
Enumerable.Range
是使LINQ库完整的必要条件(否则你如何实现for(int i ...)
循环呢?) - digEmAll