RSS

日別アーカイブ: 2013/12/10

Advent LINQ (10): .NET 2.0でLINQを使う

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

なお、linqbridgeはPLINQをサポートしていない。AsParallel()から始まるPLINQを用意するのは複雑すぎる。自前で実装するのは不可能ではないが、そんな事を考えるならC# 3.0に移行する事を真剣に考えた方が良い。どうしても並列実行を諦めることが出来ないのなら、TPLの自前実装で我慢しよう。Palallel.ForEach()なら、自力でも実装できるはずだ。

広告
 
コメントする

投稿者: : 2013/12/10 投稿先 .NET, LINQ

 
 
%d人のブロガーが「いいね」をつけました。