プロパティのget もしくは setアクセサのどちらかにアクセス修飾子を指定することでアクセス制御が別個にできるようになった。次の例では、getアクセサはpublic、setアクセサはprivateである。public class MyClass{ private string status = string.Empty; public string Status { get { return status; } private set { status = value; } }}
nullを保持できる値型、Nullableが導入された。int? i = 512;i = null; int? j = i + 500; //jはnullとなる。nullとの演算の結果はnullになる。
int?はNullable<int>の糖衣構文である。また、nullを保持しているNull許容型のインスタンスをボックス化しようとすると、単に空参照 (null) に変換される[4]。int? x = null;object o = x;System.Console.WriteLine(o == null); //Trueが出力される
また、null結合演算子 (??)が導入された。これは、nullでない最初の値を返す。object obj1 = null;object obj2 = new object();object obj3 = new object();return obj1 ?? obj2 ?? obj3; // obj2 を返す
この演算子は主にNullable型を非Nullable型に代入するときに使われる。int? i = null;int j = i ?? -1; // nullをint型に代入することはできない
var キーワードが導入され、型推論を利用したローカル変数の宣言ができるようになった。var s = "foo";// 上の文は右辺が string 型であるため、次のように解釈される:string s = "foo";// 以下に挙げる文は誤りである:var v; // 初期化式を欠いている (型を推論する対象が存在しない)var v = null; // 型が推論できない (曖昧である)
拡張メソッドが導入された。既存のクラスを継承して新たなクラスを定義することなく新たなインスタンスメソッドを追加定義することができる。具体的には、独自の静的クラス内に this 修飾子をつけた、拡張メソッドを追加する対象の型の引数を最初に持つメソッドを定義することによって、通常の静的メソッドとしての呼び出しの他に指定した型のインスタンスメソッドとしての呼び出しを行うことができるメソッドを作ることができる。以下に例を挙げる:public static class StringUtil{ public static string Repeat(this string str, int count) { var array = new string[count]; for (var i = 0; i < count; ++i) array[i] = str; return string.Concat(array); }}
この例は string 型に、文字列 (string 型のインスタンス) を指定した回数繰り返したものを返すメソッド Repeat を追加している。このメソッドは、以下のように呼び出すことができる:// 静的メソッドとしての呼び出しStringUtil.Repeat("foo", 4);// 拡張メソッドとしての呼び出し"foo".Repeat(4);// (どちらの例も "foofoofoofoo" を返す)
また、列挙型にインスタンスメソッドを追加することも可能である。以下に例を挙げる:public enum Way{ None, Left, Right, Up, Down} public static EnumUtil{ public static Way Reverse(this Way src) { switch(src) { case Way.Left: return Way.Right; case Way.Right: return Way.Left; case Way.Up: return Way.Down; case Way.Down: return Way.Up; default : return Way.None; } }}
このメソッドは以下のように呼び出すことができる:Way l = Way.Left;Way r = l.Reverse(); // Way.Right
部分メソッドが導入された。部分型(partial 型)内で定義された private で、かつ戻り値が void のメソッドに partial 修飾子をつけることでメソッドの宣言と定義を分離させることができる。定義されていない部分メソッドは何も行わず、何らエラーを発生させることもない。例えば:partial class Class{ partial void DebugOutput(string message); void Method() { DebugOutput("Some message"); Console.WriteLine("Did something."); }}
上のコードにおいて Method() を呼び出すと、Did something. と表示されるだけだが、ここで以下のコード:partial class Class{ partial void DebugOutput(string message) { Console.Write("[DEBUG: {0}] ", message); }}
を追加した上で Method() を呼び出すと、[DEBUG: Some message] Did something. と表示される。
匿名メソッドをより簡略化した記法として、ラムダ式が導入された。以下の匿名メソッドdelegate (int i) { return i + 1; }
は、ラムダ式を使って次のように記述できる:(int i) => i + 1; /* 式形式のラムダ *///或いは:(int i) => { return i + 1; }; /* ステートメント形式のラムダ */
ラムダ式は匿名メソッドと同様に扱えるが、式形式のラムダがExpression<TDelegate>型として扱われた場合のみ匿名メソッドとして扱われず、コンパイラによって式木を構築するコードに変換される。匿名デリゲートが実行前にコンパイルされたCILを保持するのに対し、式木はCILに実行時コンパイル可能であるDOMのような式の木構造そのものを保持する。これはLINQクエリをSQLクエリなどに変換する際に役立つ。
以下は、3つの任意の名前の変数、整数、括弧、及び四則演算子のみで構成された式を逆ポーランド記法に変換する汎用的なコードである:public static string ToRPN(Expression<Func<int, int, int, int>> expression){ return Parse((BinaryExpression) expression.Body).TrimEnd(' ');} private static string Parse(BinaryExpression expr){ string str = ""; if (expr.Left is BinaryExpression) { str += Parse((BinaryExpression) expr.Left); } else if (expr.Left is ParameterExpression) { str += ((ParameterExpression) expr.Left).Name + " "; } else if (expr.Left is ConstantExpression) { str += ((ConstantExpression) expr.Left).Value + " "; } if (expr.Right is BinaryExpression) { str += Parse((BinaryExpression) expr.Right); } else if (expr.Right is ParameterExpression) { str += ((ParameterExpression) expr.Right).Name + " "; } else if (expr.Right is ConstantExpression) { str += ((ConstantExpression) expr.Right).Value + " "; } return str + expr.NodeType.ToString() .Replace("Add", "+") .Replace("Subtract", "-") .Replace("Multiply", "*") .Replace("Divide", "/") + " ";} // 呼び出し例:ToRPN((x, y, z) => (x + 1) * ((y - 2) / z)); // "x 1 + y 2 - z / *" を返す
オブジェクトの初期化が式として簡潔に記述できるようになった。var p = new Point { X = 640, Y = 480 };// 上の文は次のように解釈される:Point p = new Point();p.X = 640;p.Y = 480;
また、コレクションの初期化も同様に簡潔に記述できるようになった。var l = new List<int> {1, 2, 3};var d = new Dictionary<string, int> {{"a", 1}, {"b", 2}, {"c", 3}};// 上の文は次のように解釈される:List<int> l = new List<int>();l.Add(1);l.Add(2);l.Add(3);Dictionary<string, int> d = new Dictionary<string, int>();d.Add("a", 1);d.Add("b", 2);d.Add("c", 3);
プロパティをより簡潔に記述するための自動実装プロパティが導入された。プロパティの定義に get; set; と記述することで、プロパティの値を保持するための匿名のフィールド (プログラマは直接参照することはできない) と、そのフィールドにアクセスするためのアクセサが暗黙に定義される。また,get;とset;のどちらか片方だけを記述することは出来ない。以下のコード:public int Value { get; set; }
は、以下のようなコードに相当する動作をする:private int __value;public int Value{ get { return __value; } set { __value = value; }}
但し、上のコードでは匿名のフィールドに便宜的に __value と命名している。実際はプログラマはこのフィールドにアクセスすることはできない。
一時的に使用される型を簡単に定義するための匿名型が導入された。以下に例を挙げる:new { Name = "John Doe", Age = 20 }
上の式は、以下の内容のクラスを暗黙に定義する。定義されたクラスは匿名であるが故にプログラマは参照できない。public string Name { get; }public int Age { get; }
同じ型、同じ名前のプロパティを同じ順序で並べた匿名型は同じであることが保証されている。即ち、以下のコード:var her = new { Name = "Jane Doe", Age = 20 }var him = new { Name = "John Doe", Age = 20 }