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

Chapter6 ラムダ式(前編)

川俣 晶
2009/10/19

6.6 デリゲートの共変性と反変性

 これはラムダ式に限定されない話題だが、関連性があるので取り上げておく。

 次ページのリスト6.8のソースコードは、Visual Studio .NET 2003(以下、VS.NET 2003)ではコンパイルエラーになるが、Visual Studio 2008(以下、VS 2008)ではコンパイルできる。

using System;

abstract class Person
{
  public abstract void SayMyName();
}

class StringNamePerson : Person
{
  string name;

  public override void SayMyName()
  {
    Console.WriteLine("私が{0}です。", name);
  }

  public StringNamePerson(string name)
  {
    this.name = name;
  }
}

delegate Person PersonCreator();

class Program
{
  static StringNamePerson Target()
  {
    return new StringNamePerson("L");
  }

  static void Main(string[] args)
  {
    // オブジェクトを作成する方法を取得する
    // (VS.NET 2003ではエラーになる)
    PersonCreator creator = new PersonCreator(Target);

    // 実際にオブジェクトを作成して使用する

    creator().SayMyName(); // 出力:私がLです。
  }
}
リスト6.8 デリゲートの共変性の例(VS.NET 2003ではコンパイルエラーになる)

 ここで、「new PersonCreator(Target);」というデリゲートの作成が問題になる。PersonCreatorデリゲートの戻り値の型はPerson型であるのに対して、メソッドTargetの戻り値の型はStringNamePerson型であるためだ。

 VS.NET 2003は、これを型の不一致と見なして「メソッド 'Program.Target()' はデリゲート型 'Person PersonCreator()' と一致しません。」というエラーとする。しかし、StringNamePersonクラスはPersonクラスを継承しているため、Person型と見なして受け入れてもうまく機能する。VS 2005では、そのようにして受け入れるようになっている。これを「デリゲートの共変性」という。

 ちなみに、

PersonCreator creator = new PersonCreator(Target);

という式は、VS.NET 2003でも構文エラーにしないためにこのように記述しているが、VS 2008では、

PersonCreator creator = Target;

でかまわない。

 以上は戻り値の型の場合だが、引数の型は逆に、より強い型を指定されたメソッドを、より弱い型のデリゲートで受け付ける拡張が行われている。これを「デリゲートの反変性」という。趣旨は同様なので、実例となるサンプルコードのみリスト6.9として掲載しておく。

using System;

abstract class Person
{
  public abstract void SayMyName();
}

class StringNamePerson : Person
{
  string name;

  public override void SayMyName()
  {
    Console.WriteLine("私が{0}です。", name);
  }

  public StringNamePerson(string name)
  {
    this.name = name;
  }
}

delegate void SayObjectName(StringNamePerson p);

class Program
{
  static void Target(Person p)
  {
    p.SayMyName();
  }

  static void Main(string[] args)
  {
    // VS.NET 2003ではエラーになる
    SayObjectName say = new SayObjectName(Target);


    say(new StringNamePerson("L")); // 出力:私がLです。
  }
}
リスト6.9 デリゲートの反変性の例(VS.NET 2003ではコンパイルエラーになる)

 ちなみに、

SayObjectName say = new SayObjectName(Target);

という式は、先ほどと同様に2008では、

SayObjectName say = Target;

でかまわない。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter6 ラムダ式(前編)
    1.6.1 おかずでもデザートでもなく“ご飯”
    2.6.2 ラムダ式とは何か?/【C#olumn】定義済みデリゲートを活用しよう
    3.6.3 ラムダ式は上位スコープにアクセスできる
    4.6.4 キャプチャされる変数
    5.6.5 注意を要するキャプチャの本質
  6.6.6 デリゲートの共変性と反変性
    7.6.7 デリゲートインスタンスの等価性
    8.6.8 ラムダ式で継承を置き換えてみる
    9.6.9 C# 2.0と匿名メソッド/【Exercise】練習問題
 
インデックス・ページヘ  「[完全版]究極の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 記事ランキング

本日 月間