C# 3.0 による .NET 2.0 アプリケーション開発 - XNA で LINQ を使おう
Visual Studio 2008 の新機能のひとつに「Multi-Targeting」があります.これは,Visual Studio 2008,つまり C# 3.0 や Visual Basic 9 を用いつつ,ターゲット環境を .NET 2.0/3.0/3.5 から選択できる,というものです.
C# 3.0 や Visual Basic 9 の新機能は,基本的にコンパイル時のシンタックスシュガーであり,そのうちいくつかの機能は System.Core.dll に定義された型に依存しています.
var キーワード,匿名関数としてのラムダ式などは,ターゲットとして .NET 2.0 を選んだ場合にも使うことができる機能の一例です.
一方,System.Core.dll に依存する言語新機能は,.NET 2.0 をターゲットとするプロジェクトでは使うことができません.表向きは.
.NET 2.0 アプリケーションで LINQ を使う
LINQ は System.Core.dll に定義された型を使用します.例えば System.Func delegate,LINQ のクエリ演算子を提供する System.Linq.Enumerable クラス,拡張メソッドであることを示す System.Runtime.CompilerServices.ExtensionAttribute などがそれにあたります.これらの型は,C# コンパイラがコンパイル時に行うコード変換に用いられて,コンパイル時に見つからないとコンパイルエラーになります.
さてここで問題です.ターゲットとして .NET 2.0 を選びつつ,これらの足りない型を自前で用意してみたらどうなるでしょうか?
using System; using System.Collections.Generic; using System.Text; namespace System { public delegate TResult Func<TResult>(); public delegate TResult Func<T, TResult>(T arg); public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2); public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3); public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class ExtensionAttribute : Attribute { } } namespace System.Linq { public static class Enumerable { public static IEnumerable<TResult> Select<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, TResult> selector) { foreach (var item in source) { yield return selector(item); } } public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { return new List<TSource>(source); } } } namespace ConsoleApplication1 { using System.Linq; class Program { static void Main(string[] args) { var array = new []{1, 2, 3, 4}; var seq = from x in array select x; seq.ToList().ForEach(Console.WriteLine); } } }
答えはなんと「コンパイルは成功する」が csc.exe の挙動としては正解です.(規格がこの辺についてためになる一言を含んでいるかは未調査)
この例では Enumerable.Select のみを実装していますが,他の拡張メソッドも順次再実装することで,LINQ to Object のそれなりの部分はカバーできるでしょう*1.昔の sequence.cs を保存していた人はおめでとう,ですな.あとはまあビルドプロセスをちょこっと工夫すれば,LINQ を使って XNA 開発なんてのもありでしょうね.
もっとも,本来 System.Core.dll に定義されているはずの型を詐称するわけで,.NET 3.5 のアセンブリと混ぜるな危険の暗黒面.ご利用は計画的に.
追記 (2008年8月6日)
2008年8月6日現在,Microsoft 以外によるいくつかの Linq to Object 実装がいくつか存在します.
ひとつは『Enumerableならあります - ものがたり』にて紹介頂いた Mono の実装です.
他のものについては『.NET Framework 2.0 で LINQ を使う方法 - NyaRuRuの日記』にてまとめて紹介してあります.
*1:Expression Trees はちょっとつらそう