追加された配列生成式の構文

暗黙的に型付けされた配列には、必ず配列初期化子array-initializerが必要になります。配列初期化子に指定されている要素の値から型を推論します。よって、型が推論できない場合やnull型になった場合はコンパイル時にエラーが報告されます。

var ary = new[] { 10, 20 ,30 }

上記の配列生成式は int型の配列を生成します。配列初期化子に指定されている要素の型から、コンパイラは配列の型を推論することができます。互換性のない型の要素が指定されている場合はエラーとなります。例えば、整数と文字列の組み合わせなどです。object型の配列の生成は明示的に行わなければなりません。

拡張メソッド

拡張メソッド Extension methodsは、完成されたクラスの内部を変更することなく、外部からメソッドを加える機能です。これは、特定のクラスに外部のメソッドを加えるというよりも、ある型に関連した処理を行うメソッドと型を関連付ける仕組みと考えた方が良いでしょう。技術的には、staticで宣言されたメソッドをインスタンス・メソッドの構文で呼び出せるようにするだけです。

拡張メソッドを宣言するには、非ジェネリックで static修飾子を持つ静的クラスの中で、メソッドの最初のパラメータにthis修飾子を指定します。拡張メソッドは、インスタンス・メソッドの構文で呼び出せるという点を除いて、機能的には通常のstaticメソッドと変わりありません。

例えば、文字列型を拡張するメソッドを次のように宣言することができます。

public static void Print(this string str);

メソッドの仮パラメータに注目してください。str パラメータの型stringの前にthisキーワードが修飾されています。これによって、この拡張メソッドが宣言されているクラスがインポートされているスコープ内であれば、Print() メソッドを string 型オブジェクトのインスタンス・メソッドのように呼び出すことができます。

つまり、拡張メソッドとは以下のような一般的なインスタンス・メソッドの呼び出しを置き換えるものであると考えることができます。

オブジェクト . メソッド ( パラメータ ... );

上記のメソッドの呼び出しは、以下のstaticな拡張メソッドに置き換えられます。

メソッド ( オブジェクト, パラメータ ... );

結局のところ、拡張メソッドとは staticメソッドの呼び出し方法の多様化を図ったものであり、拡張とは概念レベルのものであって、実装レベルで外部からクラスの機能を拡張するようなものではありません。しかし、拡張メソッドを用いたライブラリの利用者にとっては、インスタンスに新しいメソッドが加えられたように見えるでしょう。

サンプル02

static class Test
{
    public static void Print(this string obj)
    {
        System.Console.WriteLine(obj);
    }
    static void Main(string[] args)
    {
        "Kitty on your lap".Print();
    }
}

実行結果

Kitty on your lap

サンプル02は、文字列型に対するPrint()拡張メソッドを作成しています。Main()メソッドのコードを見ると、文字列型のオブジェクトから、インスタンス・メソッドのようにPrint()メソッドを呼び出していることが分かります。しかし、生成されたアセンブリを見ると、正しく静的なPrint()メソッドの呼び出しに置き換えられていることが確認できます。

  IL_0001:  ldstr      "Kitty on your lap"
  IL_0006:  call       void Test::Print(string)

また、拡張メソッドとインスタンス・メソッドのシグネチャが衝突している場合、本来のインスタンス・メソッドの呼び出しが優先されます。

サンプル03

static class E
{
    public static void M(this A obj)
    {
        System.Console.WriteLine("E クラスの M() 拡張メソッド");
    }
}
class A
{
    public void M()
    {
         System.Console.WriteLine("A クラスの M() メソッド");
    }
}
static class Test
{
    static void Main(string[] args)
    {
        A obj = new A();
        obj.M();
        E.M(obj);
    }
}

実行結果

A クラスの M() メソッド
E クラスの M() 拡張メソッド

サンプル03では、EクラスでAクラスの拡張メソッドM()を作成していますが、AクラスはすでにM()メソッドを保有しています。そのため、AクラスのオブジェクトからM()メソッドを呼び出した場合はAクラスのM()メソッドが呼び出されます。

拡張メソッドは、実質的には単純なstaticメソッドにすぎないため、通常のstaticメソッドの呼び出し方法で呼び出すことも可能です。サンプル03では、通常のstaticメソッドの呼び出し方法でEクラスのM()メソッドを呼び出しています。