Unity & C# 学習教材

拡張メソッド

既存のクラスに新しいメソッドを追加したい場面があります。しかし、対象が外部ライブラリの型や sealed クラスなら、クラス定義そのものを編集できないことがあります。そういうときに使うのが拡張メソッドです。

学習目標

前提知識


1. 既存の型を直接編集できない場面

たとえば既存のクラスに便利なメソッドを足したくても、そのクラスの定義を変更できないことがあります。

このようなとき、別の static class にメソッドを書くことで、元の型にメソッドが増えたように扱えます。


2. 拡張メソッドの定義

書式:拡張メソッド

1
2
3
4
5
6
7
static class 拡張クラス名
{
    public static 戻り値の型 メソッド名(this 対象の型 引数名, 追加引数)
    {
        処理
    }
}
要素 説明
static class 拡張メソッドを置くクラス。必ず static
public static 拡張メソッド本体。必ず static
this 第一引数が拡張対象であることを示す
対象の型 拡張したい型
引数名 拡張先のインスタンスを受け取る名前
追加引数 必要なら第2引数以降に追加する引数

this を付けた第一引数が、「どの型を拡張するか」を決めます。


3. 呼び出し方

呼び出し側では、通常のインスタンスメソッドと同じように 変数.M() と書けます。

コンパイラはこれを、内部では AExtensions.M(a) のような static メソッド呼び出しに変換します。つまり、拡張メソッドは糖衣構文です。


4. 実行例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
    public int Value;
}

static class AExtensions
{
    public static void M(this A a)
    {
        Console.WriteLine($"AExtensions.M: {a.Value}");
    }

    public static void N(this A a, int x)
    {
        Console.WriteLine($"AExtensions.N: {a.Value + x}");
    }
}

var a = new A { Value = 42 };
a.M();
a.N(10);
1
2
AExtensions.M: 42
AExtensions.N: 52

a.M() と書いていますが、実体は AExtensions.M(a) です。a.N(10) の実体は AExtensions.N(a, 10) です。


5. 注意点

拡張メソッドは、対象クラスの内部に入り込む仕組みではありません。そのため、使えるのは対象型の public メンバー が中心です。

つまり、拡張メソッドは「見た目をインスタンスメソッドに近づける書き方」であり、アクセス制御を無視する仕組みではありません。


よくあるミス

ミス①:拡張メソッドを non-static クラスに定義する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{
    public int Value;
}

// ❌ NG: 拡張メソッドを置くクラスは static 必須
// class AExtensions
// {
//     public static void M(this A a) { Console.WriteLine(a.Value); }
// }

// ✅ OK: static class に定義する
static class AExtensions
{
    public static void M(this A a) { Console.WriteLine($"AExtensions.M: {a.Value}"); }
}

ミス②:対象クラスの private メンバーにアクセスしようとする

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A
{
    private int _value = 42;
    public int Value { get { return _value; } }
}

static class AExtensions
{
    // ❌ NG: private メンバーにはアクセスできない
    // public static void M(this A a) { Console.WriteLine(a._value); }

    // ✅ OK: public メンバーを使う
    public static void M(this A a) { Console.WriteLine($"AExtensions.M: {a.Value}"); }
}

まとめ


理解度チェック

以下の問いに答えられるか確認しましょう。

  1. 拡張メソッドが必要になるのはどのような場面ですか?
  2. 次のコードの出力結果は何になりますか?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    class A
    {
        public int Value;
    }
    
    static class AExtensions
    {
        public static void M(this A a)
        {
            Console.WriteLine($"AExtensions.M: {a.Value}");
        }
    }
    
    var a = new A { Value = 7 };
    a.M();
    
  3. AValuex を足した結果を表示する拡張メソッド N(this A a, int x) を書いてください。
解答を見る
  1. 外部ライブラリの型や sealed クラスなど、元のクラスを直接編集できないのに便利なメソッドを追加したい場面です。
  2. 1
    
       AExtensions.M: 7
    
  3. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    class A
    {
        public int Value;
    }
    
    static class AExtensions
    {
        public static void N(this A a, int x)
        {
            Console.WriteLine($"AExtensions.N: {a.Value + x}");
        }
    }
    
    var a = new A { Value = 10 };
    a.N(5);
    
    1
    
    AExtensions.N: 15
    

次のステップ

継承 では、既存クラスのメンバーを引き継いで新しいクラスを作る仕組みを学びます。