連載:C# 4入門

第6回 in/outキーワードと共変性と反変性

株式会社ピーデー 川俣 晶
2010/12/17
Page1 Page2 Page3

両方同時に使えるか?

 最初に書いたとおり、outなら「共変」、inなら「反変」、どちらでもないなら「不変」である。しかし、これは1つの型パラメータについての話であり、1つのデリゲートの中に共変の定義と反変の定義が混在しても問題はない。そのような込み入った事例はレアかといえばそうではなく、使用頻度の高いSystem名前空間のFuncデリゲートがそのような事例そのものになっている。

 例えば、Func<T,TResult>デリゲートの定義は、厳密には以下のとおりになっている。

public delegate TResult Func<in T, out TResult>(T arg)
Func<T, TResult>デリゲートの定義

 このとおり、inとoutの共存である。だから、以下のようなコードも可能であり、コンパイル&実行可能だ。

using System;

class Program
{
  private static void output(Func<string, object> proc)
  {
    Console.WriteLine(proc("Hello C#"));
  }

  static void Main(string[] args)
  {
    Func<object, string> proc = (s) =>
      {
        Console.WriteLine(s);
        return "succeeded";
      };
    output(proc);
  }
}
リスト6

Hello C#
succeeded
リスト6の実行結果

 ここでoutputメソッドが待っている引数はFunc<string, object>型だが、実際に渡されるのはFunc<object, string>型である。しかし問題はない。これは変換できるからである。

 これにより、日常的に使い慣れているFuncデリゲートの利用範囲も拡大することができる。

 ちなみに、Funcデリゲートなんて知らないという古いC#プログラマーであっても、Visual Studio 2010にアップグレードすれば効能がある。実はPredicateデリゲートにもinキーワードが付加されている。

public delegate bool Predicate<in T>(T obj)
Predicate<in T>デリゲートの定義

 つまり、Predicateデリゲートで書いてしまったC# 2.0時代のソースであろうと、C# 4に持ち込むと反変性のおかげで何ら修正することなく、応用範囲が拡大するわけである。

返却値ではない返却に注意

 実験中に筆者のVisual Studio 2010で実際に遭遇した問題だが、以下のようなコードは通りそうでいて通らなかった。

public interface ISample2<out T>
{
  void GetValue(out T val);
}
リスト7

 念のために説明すると、最初のoutキーワードと2つめのoutキーワードの意味は異なっている。最初のoutキーワードは、今回説明するin/outキーワードのoutだが、2つめのoutキーワードは出力専用の参照渡しを意味するキーワードである。

 やはりin/outは自分で記述するより、クラス・ライブラリの活用に専念した方がよさそうである。

まとめ

 今回のまとめ。

  • C# 4にはin/outキーワードが拡張されている
  • C# 4ではジェネリックの型パラメータの共変性と反変性が拡張されている
  • 実際にin/outキーワードを書く機会は少なそうだが、それを理解してクラス・ライブラリを使いこなすと効果は大きい
  • 実際には、共変、反変、不変、の3つのケースがある。共変でも反変でもない場合は不変と呼ばれる

 ともかく自分でin/outキーワードを書かなくても、普通にコードを書いていると、すぐにそれを定義に含むクラス・ライブラリの機能を使うことになる。利用頻度の高いAction<T>デリゲートにすら定義が付いているのだから、活用する気になればすぐ使えるぐらい身近だ。共変性と反変性を意識すれば、込み入った複数の型の処理を統合でき、コードをシンプルにまとめられる。だが、意識しなくてもコードを書けるし、動いてしまう。せっかく目の前に「可能性」が用意されていても、それを見過ごして行ってしまえば可能性は希望に変わらない。動くからそれでいいと思うのはやめよう。遠いように見えるが、そうではない。贈り物は手を伸ばせばすぐつかめるぐらい近くまで来ているのだ。可能性を本物の希望に変えよう。

 以下は余談だ。

 本の箱少々を倉庫に預けていたが、本を預ける倉庫代もバカにならないし、見ない本を預けておく意味はないと思って倉庫から引き上げて、ほとんど処分してしまった。残ったのはほんのわずかだ。その中にTurbo Pascal 3.0のMS-DOSジェネリック(英語)版のマニュアルがあった。一時は、これを主力開発言語として使っていたともある筆者にとっては、懐かしいものだ。このマニュアルにフロッピーディスクを挟んでシュリンクラップして秋葉原の亜土電子で売っていたと思うが、記憶は定かではない。ちなみに、亜土電子はT-Zoneの前身となる秋葉原のショップである。

 このような話を長々と書いたのには、もちろん理由がある。実は、C#の生みの親であるアンダース・ヘルスバーグ氏が昔、ボーランドで開発したのが、このTurbo Pascalなのである。つまり、筆者が紆余曲折していろいろな言語を経験して最終的に戻ってきたのは、やはりアンダース・ヘルスバーグ氏の言語であったということだ。実に感慨深いものがある。

 だから筆者にとってのC#は、実は『悪の帝国(笑)マイクロソフト』の開発言語という以前に、昔使った『反マイクロソフトの代表選手(笑)の後継言語』であったわけだ。いや、それだけではない。Pascal系の言語を作るということは、反C言語文化の代表選手でもある。

 C#も、C言語風の構文を与えられているが、実は細部を見ていくとCよりPascalに近い部分が多くある。そういう意味で、時代遅れで硬直的な秩序でしかないC言語文化の担い手はC#を使いこなせないかもしれないが、それはC言語文化に世界観が染まりすぎているからだろう。実際に世界はもっと広く、自由である。その広大さを実感させてくれるのが、やはりC#であり、アンダース・ヘルスバーグ氏なのだろう。昔懐かしいTurbo Pascalのマニュアルを見ながらそう思う。End of Article

 

 INDEX
  C# 4入門
  第6回 in/outキーワードと共変性と反変性
    1.ジェネリック使用時の制約
    2.実は単純ではないジェネリック/in/outキーワードの使いどころ
  3.両方同時に使えるか?/返却値ではない返却に注意/まとめ
 
インデックス・ページヘ  「C# 4入門」


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 記事ランキング

本日 月間