はじめに
C# 3.0の情報や話題も徐々に増えてきてましたし、実際に試されている方も多いことと思います。DLINQやXLINQなどのテクノロジもその姿を現しました。しかし、たまに「C# 3.0で追加された言語仕様はどれもLINQを実現するためのようなもの」といった発言や書き込みを目にします。確かに実現には必要ですが、これは少しショック!
ということで、C# 3.0の言語仕様自体に興味を持っていただくべく投稿初挑戦です。これらがどのような新しいスタイルを導くのかをお見せできればと思います。では、未熟者ですがよろしくお願いします。
対象読者
- C# 3.0の言語仕様に興味のある方
- 関数プログラミングに興味のある方
- やさしい未婚女性(お料理が好きだとうれしい)
C# 3.0 言語仕様解説
本題に入る前に、まずざっと追加分の仕様を紹介したいと思います。あまり細かい部分までは説明しきれませんので、詳細は仕様書などを参照してください。
ローカル変数の暗黙的な型付け
C# 3.0では、型推論の恩恵を受けてローカル変数の型をvarキーワードで置き換えることができます。
var v1 = 1024L; //v1はlong型 var v2 = new List<int>();
型を判断することができなくなってしまうため、宣言時に必ず値を代入しなくてはなりません。また、nullを代入することもできません。
単純であるため軽視されがちのように思いますが、これはなかなか重要な仕様のように思います。理由は後述します。
拡張メソッド
ちょっと特殊な(?)staticメソッドを用意しておくと、従来の使い方に加え、既存の型のインスタンスメソッドであるかのように使うことができます。 以下のようにstatic class内に宣言します。
public static class MyExtensions { public static int Foo( this int v1, int v2 ) { return v1 + v2; } public static void Print<T>( this T v ) { Console.Write( v ); } }
あまり面白い例ではありませんが、引数部分に注目してください。メソッドの第1引数の前にthisキーワードが付いています。 こうすることで、第1引数の型のメソッドであるかのように呼ぶことができます。以下に例を示します。
int value1 = 98; int value2 = value1.Foo( 2 ); // value2 == 100。 // value1がFooメソッドの第一引数に相当 Math.Pow( 2, 10 ).Print(); // 1024と表示 MyExtentions.Print( "hello !" ); // 普通に使うことも可能
仕様書にも書いてありますが、むやみやたらに使うことは推奨されていません。
プロパティの自動実装
public class Foo { public int Value1{ get; set; } public string Value2{ get; set; } // アクセシビリティを変えることも可能 public byte Value3{ get; private set; } }
このように書くと、privateな変数とアクセッサを自動で用意してくれるそうです。
初期化いろいろ
コレクションクラスを、従来より簡単に初期化できるようになりました。
List<int> list = new List<int>{ 1, 2, 4, 8, 16, 32, 64, 128 };
他に、初期化時にプロパティ/メンバ変数の値を代入できるようになりました。
// 「プロパティの自動実装」で掲示したFooクラス Foo f = new Foo{ Value2 = "foooo", Value1 = 100 };
匿名型
匿名メソッドの時と同じように、今度はその場でクラスを作れるようになりました。 プロパティのみ持たせることができます。
var anonymousType = new { Name = "匿名", Age = 19 }; Console.WriteLine( anonymousType.GetType() ); var at2 = new { Name = "a", Age = 99 };
<>f__AnonymousType0`2[System.String,System.Int32]
1行目のようにして、作りたいプロパティとその値を渡します。 出力を見ると分かるように、実際にはコンパイラが<>f__AnonymousType0
というエキサイティングな名前のクラスを生成しています。わざわざクラスや構造体を宣言するまでもないけど、データをひとまとめにしたい!という場合に有効です。Pythonなどに見られるタプルとも捉えることができます。
我々はコンパイラがどんなクラス名で展開するのか知りませんし、そもそもコーディング時にはそのようなクラスはまだありませんので、先ほど紹介したvarを使ってインスタンスを捕まえます。
なお、同じプロパティを持つ匿名型を作成した場合(上記の例だと、anonymousType
とat2
)は、同一のクラスのインスタンスとなります。ただし、プロパティの順序が入れ替わっただけでも他のクラスが生成されてしまいます。
配列の暗黙的型付け
var ar = new[]{ 'a', 'b', 'c' }; // char[]
この例ではvarを使っていますが、もちろんchar[] ar
と宣言してもかまいません。 これも渡された値から型を推論しています。
これだけだと大してありがたみを感じないような気もしますが、先ほどの匿名型の配列を用意したい場合はこれが必要になってきます。
ラムダ式
C# 2.0で追加された匿名メソッドを、より簡潔に書くことができるようになりました。ラムダ式と聞くとラムダ計算や関数型言語などを連想する方がいるもしれません。まずは試しにCommon Lispの例を見てみます。
(setf f #'(lambda (x) (expt 2 x))) (funcall f 10)
おびただしい量の括弧です。しかし、C# 3.0のラムダ式も負けてはいません。次のようになります。
Func<int, int> f = (((x)=>((((x)>10))?(((x)+x)):((x*(x)))))); f( 9 );
では早速見ていきます。まずは従来の匿名メソッドとラムダ式とを比べてみます。
Func<int,int> f1 = delegate( int x ){ return x * x; }; Func<int,int> f2 = (int x) => x * x; f1( 9 ) f2( 9 );
上のf1
とf2
は同じ働きをします。ちなみにFunc
は標準のジェネリックデリゲートです。C# 3.0のラムダ式には結構いろいろな書き方があり、次のように書くことも可能です。
Func<int,int> f3 = (x) => x * x; // 型推論のおかげでxの型を省略できる Func<int,int> f4 = x => x * x; // 括弧も省略可能 Func<int,int> f5 = x => { var y = x; return y * y; }; Func<int> f6 = () => 10; Func<int,int,int> f7 = (x, y) => x * y;
複数行必要な場合はf5
のようにブロックで囲い、きちんとreturnする必要があります。また、引数がない場合(f6
)や複数個の引数を取る場合(f7
)は括弧を省略できません。
もちろん書き方は自由です。個人的には、( (x) => x * x );
のように、ラムダ式をひとまとめにして書いたりしています。
ラムダ式が匿名メソッドと違うもう1つの点は、ラムダ式は式自体を動的に組み替えることが可能だという点です。本題からそれるためここでは割愛しますが、興味のある方はSystem.Linq.Expressions名前空間などを参照してみてください。