Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders posts can be found here.
Thanks for all of your patience while I’ve been dealing with other matters these last few weeks. I didn’t want to let my post slide a third week, so I decided to say a few words about a small static method in the Enumerable class from System.Linq.
Using Range() to generates a sequence of consecutive integers
So, if we look in the Enumerable static class, where most of the linq extension methods are defined, we will also see a static method called Range() whose purpose is to generate a range of integers from a given start value and for a given count.
It’s syntax looks like:
- Range(int start, int count)
- Returns a sequence of int from start to (start + count – 1).
So, for example, the code snippet below will create an List<int> of the numbers from 1 to 10:
1: var numbers = Enumerable.Range(1, 10).ToList();
So, this seems simple enough, right? Well, yes, it is a handy way to create a sequence of consecutive int values that you can use directly, but when coupled with other constructs, it has many other uses as well.
Using Range() to feed a more complex LINQ expression
For example, if we wanted a list of the first 5 even numbers, we could start with the number of expected items and multiply up by our step factor in a Select():
1: // take sequence 0, 1, 2, 3, 4 and multiply each by two...
2: var evens = Enumerable.Range(0, 5).Select(n => n*2).ToList();
Or, you could create a range over the total range of values and use Where() to filter it down:
1: // generates sequence from 0..9, but only selects even ones
2: var odds = Enumerable.Range(0, 10).Where(n => (n % 2) == 0).ToList();
But the great thing about Range() is you don’t have to use it to just produce numbers, you can use the sequence it generates either directly or as the starting point for a more complex LINQ expression.
For example, if we wanted to generate a series of strings for font sizes we want to allow in a windows form, we could do that easily:
1: // takes the range from 1 to 10 and multiples by 10 and puts % on end.
2: var percentages = Enumerable
3: .Range(1, 10)
4: .Select(i => (i * 10) + " pt")
This would give us an array of strings containing “10 pt”, “20 pt”, “30 pt”, … “100 pt”.
So Range() comes in handy for any expression you can create that starts with a simple sequence of integers. But what else?
Using Range() to generate multiple complex objects
One other use of Range() that can come in handy is for repeating some action multiple times. For example, say we want to create and start several Task instances for some parallel work.
We could do this using a standard array allocation and for loop:
1: // first construct the array of the appropriate size
2: Task tasks = new Task[NumConsumers];
4: // then loop through each index and create the new instance
5: for (int i = 0; i < NumConsumers; i++)
7: tasks[i] = TaskFactory.StartNew(SomeAction);
Or we could do it in one LINQ expression:
1: // constructs an array of NumConsumers tasks that will each
2: // perform SomeAction in parallel
3: Task tasks = Enumerable.Range(1, NumConsumers)
4: .Select(i => TaskFactory.StartNew(SomeAction))
Now, you may be wondering why we can’t do this instead:
1: // Enumerable.Repeat() repeats a value the specified number of times
2: Task tasks = Enumerable.Repeat(TaskFactory.StartNew(SomeAction), NumConsumers)
The answer is that Enumerable.Repeat() repeats the given value the specified number of times, so in the example above, it would call TaskFactory.StartNew() once and then repeat the resulting reference NumConsumers times, which is clearly not what we want! There are ways to do similar things with Repeat(), but they’re not quite as straightforward as using Range().
The Enumerable.Range() method performs a very simple function, but it’s results can be used to drive much more complex LINQ expressions. Feel free to use it for generating simple int sequences all the way to generating sequences of a repeated action. Either way, it’s a good tool to keep handy.