連載:[完全版]究極のC#プログラミング

Chapter14 拡張メソッド

川俣 晶
2010/03/01

14.9 メソッド呼び出しと型の関係

 拡張メソッドを呼び出す場合、注意すべきは、異なるパラメータを持つメソッドは別のメソッドと見なされるという点である。つまり、同じ名前のメソッドがすでに存在するため拡張メソッドを呼び出せないケースでも、パラメータの型を変えると呼び出せてしまうケースが存在する

 また、その型を包含する基本的な型を持つパラメータがあれば、異なる型でも拡張メソッドは呼び出されないかもしれない。

 それを具体的に見るために、次のサンプルコード、リスト14.10を作成した。

using System;
using X;

namespace X
{
  public static class A
  {
    // 拡張メソッド
    public static void MyMethod(this object obj, int i)
    {
      Console.WriteLine("A.MyMethod〜int called");
    }

    // 拡張メソッド
    public static void MyMethod(this object obj, string s)
    {
      Console.WriteLine("A.MyMethod〜string called");
    }
  }

  class B
  {
    public void MyMethod(int i)
    {
      Console.WriteLine("B.MyMethod〜int called");
    }
  }

  class C
  {
    public void MyMethod(object obj)
    {
      Console.WriteLine("C.MyMethod〜object called");
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var b = new B();
      var c = new C();

      b.MyMethod(1);            // 出力:B.MyMethod〜int called
      b.MyMethod(string.Empty); // 出力:A.MyMethod〜string called
      c.MyMethod(1);            // 出力:C.MyMethod〜object called
      c.MyMethod(string.Empty); // 出力:C.MyMethod〜object called
    }
  }
}
リスト14.10 メソッド呼び出しと型の関係

 以下、4つのメソッド呼び出しがどのように解釈されているかを説明する。

  • クラスBに整数のパラメータを受け取るメソッドがあるので、それが呼び出される
  • パラメータとして文字列を受け取るメソッドは存在しないので、クラスAの拡張メソッドが呼ばれる
  • object型はint型から見て基本的な型となるので、パラメータをobject型に変換して(ボックス化して)、クラスCのメソッドを呼び出している
  • int型がstring型に変わるだけで、上の結果と同じ

 このような特徴は、たとえば、引数を持つToStringメソッドが存在しないクラスに対して、書式指定の引数を持つToStringメソッドを追加する、といった使い方が可能であることを示す。

 次のリスト14.11は、クラスに書式指定機能付きのToStringメソッドを追加した例である。引数に「u」を付けた場合は、型名を大文字に変換して返す。「l」の場合は、小文字にして返す。

using System;
using X;

namespace X
{
  public static class A
  {
    // 拡張メソッド
    public static string ToString(this Target obj, string format)
    {
      switch (format)
      {
        case "u": return obj.ToString().ToUpper();
        case "l": return obj.ToString().ToLower();
        default: return obj.ToString();
      }
    }
  }

  public class Target
  {
  }

  class Program
  {
    static void Main(string[] args)
    {
      var b = new Target();
      Console.WriteLine(b.ToString()); // 出力:X.Target
      Console.WriteLine(b.ToString("u")); // 出力:X.TARGET
      Console.WriteLine(b.ToString("l")); // 出力:x.target
    }
  }
}
リスト14.11 クラスに書式指定機能付きのToStringメソッドを追加

 もちろん、このような事例は、元クラスを修正してメソッドを追加するか、継承を用いてメソッドを追加するのが筋である。拡張メソッドを使わないで済めばそのほうがよい。

 しかし、ソースを修正できず、シールクラスなどの理由により継承も封じられた状況でこれが可能なのは、ありがたい特徴ではないだろうか?


 INDEX
  [完全版]究極のC#プログラミング
  Chapter14 拡張メソッド
    1.14.1 C# 2.0プログラマーの悲劇
    2.14.2 Allメソッドを利用するのに必要な記述
    3.14.3 拡張メソッドの概要
    4.14.4 スイッチなしで機能する例
    5.14.5 sealedクラスを拡張する
    6.14.6 拡張メソッドはオブジェクト内部に手出しできない
    7.14.7 拡張メソッドはオブジェクトの振る舞いを変更できない
    8.14.8 拡張メソッドが安全である理由
  9.14.9 メソッド呼び出しと型の関係
    10.14.10 thisの正体
    11.14.11 拡張メソッドを使用すべきとき
    12.14.12 コレクションに拡張されるメソッド
    13.14.13 なぜ「using System.Linq;」なのか?/練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間