部分型()が導入された。以下のようにクラスや構造体の宣言にpartial修飾子をつけることで、その宣言を分割することができる。
partial class MyClass { int a; }
partial class MyClass { int b; }
これは以下と同義である:
class MyClass { int a; int b; }
プロパティの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許容型とnull結合演算子
nullを保持できる値型、Nullableが導入された。
int? i = 512;
i = null;
int? j = i + 500; //jはnullとなる。nullとの演算の結果はnullになる。
int?はNullableの糖衣構文である。また、nullを保持しているNull許容型のインスタンスをボックス化しようとすると、単に空参照 (null) に変換されるnull MSDNライブラリ。
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型に代入することはできない
C# 3.0からの仕様
varキーワード
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
以下は、3つの任意の名前の変数、整数、括弧、及び四則演算子のみで構成された式を逆ポーランド記法に変換する汎用的なコードである:
public static string ToRPN(Expression> 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 {1, 2, 3};
var d = new Dictionary ;
// 上の文は次のように解釈される:
List l = new List();
l.Add(1);
l.Add(2);
l.Add(3);
Dictionary d = new Dictionary();
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; }
}
一時的に使用される型を簡単に定義するための匿名型が導入された。以下に例を挙げる:
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 }
において、her.GetType() == him.GetType() は true である。
配列宣言の型省略
new キーワードを用いた配列の宣言の際、型を省略できるようになった。匿名型の配列を宣言する際に威力を発揮する。
var a = new[] {"foo", "bar", null};
// 上の文は次のように解釈される:
string[] a = new string[] {"foo", "bar", null};
// 以下の文:
var a = new[] {"foo", "bar", 123};
// は次のように解釈されることなく、誤りとなる:
object[] a = new object[] {"foo", "bar", 123};
クエリ式
LINQ をサポートするために、クエリ式が導入された。これは SQL の構文に類似しており、最終的に通常のメソッド呼び出しに変換されるものである。以下に例を示す:
var passedStudents =
from s in students
where s.MathScore + s.MusicScore + s.EnglishScore > 200
select s.Name;
上のコードは以下のように変換される:
var passedStudents = students
.Where(s => s.MathScore + s.MusicScore + s.EnglishScore > 200)
.Select(s => s.Name);
規格によると、C#は「C Sharp」(シーシャープ)と発音し、「C#」(LATIN CAPITAL LETTER C (U+0043)の後にNUMBER SIGN # (U+0023))と書く。音楽のシャープ(♯, MUSIC SHARP SIGN (U+266F))ではなくナンバーサイン (#)を採用したのは、フォントやブラウザなどの技術的な制約に加え、標準的キーボードには前者の記号が存在しないためである。