LINQは、正式には.NET 3.5 / C# 3.0でサポートされた。しかし、強力な柔軟性と拡張性の高さを、.NET 2.0のプロジェクトでも使いたいと思う事がある。実は、工夫さえすれば、.NET 2.0環境でもLINQを使う事が出来る。
LINQを使うためには、2つの要件を満たす必要がある。
- C#コンパイラが、LINQクエリ構文を解釈出来るようにする。
これを満たすためには、C# 3.0以上のコンパイラを使用すればよい。つまり、Visual Studio 2008以上の環境を使えば、コンパイラはfrom・where・select・orderbyなどのLINQクエリ構文や、ラムダ式を解釈可能になる。例え、コンパイラのターゲットが.NET 2.0に設定されていても、これらの構文は解釈可能だ。 - LINQの拡張メソッド群(Enumerableクラスなど)が必要。
これは当然、.NET 2.0のmscorlib.dllやSystem.dllには含まれていない。従って、何とかして用意する必要がある。
前者の要件に絡む、分かりにくい問題がある。「拡張メソッド」の扱いだ。C# 3.0以上のコンパイラを使えば、拡張メソッド構文は解釈可能だ。但し、拡張メソッドが定義されたクラスは特殊な扱いを受ける。
// 独自実装のEnumerable [Extension] // <-- Extension属性 public static class Enumerable { // 独自実装のWhere [Extension] // <-- Extension属性 public static IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, Func<T, bool> predict) { foreach (var value in enumerable) { if (predict(value) == true) { yield return value; } } } }
C# 3.0以上のコンパイラで拡張メソッドを定義すると、暗黙にExtensionAttribute属性がクラスとメソッドに適用される(上の例ではわざと書いた)。そのため、アセンブリを生成する際にこの拡張メソッドが見つからないと、ビルドに失敗する。当然、.NET 2.0のmscorlib.dllやSystem.dllには存在しないため、以下のようなコードも盛り込んでおく必要がある。
namespace System.Runtime.CompilerServices { // オレオレExtension属性 [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class ExtensionAttribute : Attribute { public ExtensionAttribute() { } } }
そして、こまごまとしたクラス(例えば、Func<T>やAction<T>など)と、あなたが使いたい標準的なLINQ拡張メソッド群(Where<T>やSelect<T>やOrderBy<T>等)を用意する必要がある。これらを用意すれば、「ほぼ」C# 3.5以降のLINQを模倣できる。
ほぼ、と書いたのは、どうしても回避出来ない制限が一つだけあるためだ。それは、IEnumerable<T>が、ジェネリック共変性をサポートしていない事による。これによって発生する問題は別の機会に譲るとして、実際問題、大量のLINQ拡張メソッドを自前で準備するのはなかなか難しい。
そして、同じような事を考える人は世の中にも大勢いて、NuGetでパッケージ化されていたりもするので、特に理由が無ければ、このようなパッケージを利用したほうが良いだろう。以下の「linqbridge」は、単一ソースコードのバージョンもあるので、自分のライブラリに容易に組み込みやすいだろう。
linqbridge – Re-implementation of LINQ to Objects for .NET Framework 2.0
NuGet LINQBridge
NuGet LINQBridge (Embedded)
なお、linqbridgeはPLINQをサポートしていない。AsParallel()から始まるPLINQを用意するのは複雑すぎる。自前で実装するのは不可能ではないが、そんな事を考えるならC# 3.0に移行する事を真剣に考えた方が良い。どうしても並列実行を諦めることが出来ないのなら、TPLの自前実装で我慢しよう。Palallel.ForEach()なら、自力でも実装できるはずだ。