If you're doing any non-trivial amount of work with C# and .NET, you'll be familiar with the joy that is LINQ and the set of extension method it provides. While the
Any, and more extension methods are generally well known, the
Enumerable static class also offers three non-extension methods, namely
Repeat<T>, which I want to highlight in this post.
Returning an Empty Collection:
Enumerable.Empty<T> method returns an empty enumerable which doesn't yield any values when being enumerated.
Enumerable.Empty<T> comes in very handy when you want to pass an empty to collection to a method accepting a parameter of type
What's the type that's being used by
Enumerable.Empty<int> internally? Let's find out:
Enumerable.Empty<int>().GetType() // "System.Int32"
We can see that the returned sequence is an (empty) array of integers.
For performance reasons, the returned array instance is cached for every type
T, which allows us to observe the following behavior:
Enumerable.Empty<int>() == Enumerable.Empty<int>() // True
Enumerable.Empty<int>() == Enumerable.Empty<string>() // False
A reference comparison via
== obviously returns
false for the two different arrays. The
SequenceEqual method, however, returns
true since neither sequence yields a value:
IEnumerable<object> integers = Enumerable.Empty<int>().Cast<object>();
IEnumerable<object> strings = Enumerable.Empty<string>();
bool equalByReference = integers == strings; // False
bool equalBySequence = integers.SequenceEqual(strings); // True
Generating Sequential Integers:
Some programming languages offer a shorthand notation to create a list of consecutive integers. The following code shows how this can be achieved in Haskell:
[1..5] == [1,2,3,4,5] -- True
[2..5] == [2,3,4,5] -- True
While C# doesn't define an operator similar to
.., the .NET Framework offers the static
Enumerable.Range method. It accepts two
count, and constructs a sequence of
count consecutive integers, starting at
IEnumerable<int> numbers = Enumerable.Range(1, 5);
string numberList = string.Join(",", numbers); // "1,2,3,4,5"
Note that the second parameter is the number of integers to generate, not the inclusive upper bound of the range. This is where the resulting sequence differs from the one created by Haskell's list construction syntax:
IEnumerable<int> numbers = Enumerable.Range(2, 5);
string numberList = string.Join(",", numbers); // "2,3,4,5,6"
Here's how you could use
Range to generate a string containing the English alphabet:
IEnumerable<char> letters = Enumerable
.Select(x => (char)(x + 'a'));
string alphabet = string.Join("", letters); // "abcdefghijklmnopqrstuvwxyz"
Enumerable.Range method will throw an
ArgumentOutOfRangeException if either
count is negative or
start + count - 1 is larger than
Repeating an Element:
The third and last method I want to address in this post is
Enumerable.Repeat<T>. Its signature is
Repeat<T>(T element, int count), and it creates a sequence with exactly
count occurences of the specified
IEnumerable<int> ones = Enumerable.Repeat(1, 5);
string numberList = string.Join(",", ones); // "1,1,1,1,1"
Enumerable.Repeat was conceptualized to create a sequence of a repeated value, it can be used as a driver for a generator function as well. The following snippet generates ten (pseudo-)random numbers between 0 and 1:
var random = new Random();
IEnumerable<double> randomNumbers = Enumerable
.Select(_ => random.NextDouble());
Notice that the selected value
_ isn't used at all. I like to explicitly denote that by using an underscore for the variable name. Instead,
Enumerable.Repeat is only used to repeat the number generation 10 times.
Like most methods in the
Enumerable.Repeat<T> is lazy by design to avoid computing unnecessary values: Instead of immediately returning an entirely precomputed range sequence, it returns an iterator which yields values until either the range is exhausted or the caller stops enumerating it.
Looking for More LINQ?
I encourage you to also check out ExtraLINQ, an open source project of mine which is available on NuGet, and morelinq, a library written by Jon Skeet. Both add a variety of helpful extension methods to your LINQ utility belt.