Introduction
Language INtegrated Query in C# 3.0 is pure joy to use. Once you try it, you don't want to stop.
But a few of us, for whatever reason, are still using Visual Studio 2005. In my case, I didn't want to pay $550 for the upgrade to VS2008 Pro, but I didn't want to lose features like Macros and Code Diagrams by switching to Visual C# Express Edition or SharpDevelop. So, I came up with a way I could use LINQ-to-Objects in C# 2.0.
Note: If you are stuck with .NET Framework 2.0, but you are using C# 3.0 or Visual Studio 2008, you don't need the code in this article. Just use LinqBridge.
Using the Code
Here is some simple C# 3.0 LINQ code. This code contains a query and several calls to extension methods in the System.Linq.Enumerable
class. Of course, since they are extension methods, the code doesn't actually mention Enumerable
.
using System;
using System.Linq;
class Program
{
static void Main(string[] args)
{
string[] words = new string[] {
"Pies", "Are", "Good",
"In", "Lovely", "Apples" };
Console.WriteLine(string.Join(" ", words.Take(3).ToArray()));
Console.WriteLine(string.Join(" ", words.OrderBy(x => x).ToArray()));
int[] numbers = new int[] { 4, 95, 309, 357, 233, 2 };
Console.WriteLine(numbers.Sum());
Console.WriteLine((from x in numbers where x > 300 select x).Sum());
}
}
Now, here is the equivalent code in C# 2.0, taking advantage of my PoorMansLinq
class:
using System;
using System.Collections.Generic;
using Compatibility.Linq;
class Program
{
public static void Main(string[] args)
{
string[] words = new string[] {
"Pies", "Are", "Good", "In",
"Lovely", "Apples" };
Console.WriteLine(string.Join(" ", Linq(words).Take(3).ToArray()));
Console.WriteLine(string.Join(" ", Linq(words).Sorted().ToArray()));
int[] numbers = new int[] { 4, 95, 309, 357, 233, 2 };
Console.WriteLine(Enumerable.Sum(numbers));
Console.WriteLine(Enumerable.Sum(Linq(numbers)
.Where(delegate(int x) { return x > 300; })));
}
static PoorMansLinq<T> Linq<T>(IEnumerable<T> source)
{
return new PoorMansLinq<T>(source);
}
}
Look at the first WriteLine
statement: instead of words.Take(3).ToArray()
, it reads Linq(words).Take(3).ToArray()
. Linq()
is a helper function that simply wraps an IEnumerable
object in a PoorMansLinq
object; it could have been written new PoorMansLinq<string>(words).Take(3).ToArray()
instead.
PoorMansLinq
provides most of the LINQ functionality such as Where()
, OrderBy()
, etc. It forwards all the calls to the static
class Enumerable
. PoorMansLinq
does not include all the functionality of Enumerable
:
- It does not include
static
methods such as Empty()
and Range(first, last)
that are not extension methods. - It doesn't include
AsEnumerable()
, which makes no sense without the extension methods feature. - It cannot include specializations for specific kinds of
T
, such as Average<double>()
and Sum<int>()
, because as far as I know, there is no way to do it with Generics in C# 2.0. Therefore, in order to compute the Sum, Average, Min, or Max of integers, doubles, or decimals, you need to call the method in Enumerable
directly.
PoorMansLinq
also includes Sorted()
, which is a shortcut for OrderBy(x => x)
that you see in the second WriteLine
statement. In C# 2.0, you would have to write OrderBy(delegate(string x) { return x; })
, which is cumbersome.
As I mentioned, you can't Sum numbers using the Linq(numbers).Sum()
syntax, so the third WriteLine
uses Enumerable.Sum(numbers)
instead.
The forth WriteLine
demonstrates how a simple query is translated:
from x in numbers
where x > 300
select x
becomes:
Linq(numbers)
.Where(delegate(int x) { return x > 300; })
I omitted the Select
clause, which is not needed in this case. Here's how it looks with the redundant Select
clause:
Linq(numbers)
.Where(delegate(int x) { return x > 300; })
.Select(delegate(int x) { return x; })
See this article for an introduction to the way C# 3.0 translates LINQ queries to "plain" C# 3.0. Then, the information in this article should be enough to turn it into C# 2.0.
How Did I Do It?
I started by extracting the core LINQ-to-objects code from Mono, which is open source. Then, I wrote PoorMansLinq<T>
, a wrapper around IEnumerable<T>
that provides the extension methods.
History
- May 19, 2008: Initial release
- October 27, 2010: Some functions in
PoorMansLinq<T>
that returned IEnumerable<T>
have been corrected to return PoorMansLinq<T>
instead