連載:C# 4入門

第5回 Office連携でrefキーワード不要

株式会社ピーデー 川俣 晶
2010/11/19
Page1 Page2 Page3

refキーワードの省略と引数の省略

 この問題をC#で解決する方法は2つある。

 1つは、昔のVisual Basicと同等の機能を追加することであるが、これはほとんど不可能といってよい。Visual Basicそのものですら、.NET世代に入ると、かなり大幅な変更が必要とされたので。まして出自が違うC#である。非互換の大手術を行わなければ同等にはできない。しかも、過去のソース・コードが利用できなくなってしまう。

 もう1つは、C#のC#らしさを維持しながら、汎用かつ最小限の対策で対処する方法である。この場合、以下の要素が使われる。

  • 相互運用性レイヤを改良する
  • 言語を改良する

 この方法であれば、以下の2つの条件が維持される。

  • Office本体は変更しない
  • 互換性は維持される

 では具体的に何が改良されたのだろうか。

  • dynamic型など動的なデータ扱いが強化された(汎用的な言語の改良)
  • 引数の省略がサポートされた(汎用的な言語の改良)
  • COM相互運用時のrefキーワードが省略できる(COMに特化した言語の改良)
  • refキーワード省略時は、値そのものを渡してもコンパイル可能(相互運用性レイヤの改良)

 動的な側面は利用していないが、残りの機能を使うと最初のサンプル・コード(リスト1)はここまで小さくなる(リスト2)。

using System;
using Microsoft.Office.Interop.Word;

class Program
{
  static void Main()
  {
    var app = new Microsoft.Office.Interop.Word.Application();
    app.Visible  = true;
    app.Documents.Open("sample.docx");
  }
}
リスト2

 つまりこういうことである。

  • 不必要なrefキーワードが多すぎる → 消えた
  • 変数missingが多すぎる → 省略できるので消えた
  • 変数filenameはstring型と分かっているのに、object型にしなければならない → 値を直接渡せるので、変数宣言そのものが消えた
  • 変数missingは単に省略されていることを参照渡しで示すためだけに用意されたもので、何ら特別な意図を表現していない → 引数が省略可能になったので消えた

dynamic型の価値

 最後の1つはdynamic型の価値だが、これも要素としては大きい。しかも効能を体感するのは簡単だ(dynamic型については第1回で解説している)。

 以下のコードはExcelのセルに文字列を入れる例である。まず、Visual Studio 2008つまりC# 3.0で書いてみよう。「Microsoft.Office.Interop.Excel」を参照に追加したコンソール・アプリケーションのプロジェクトである。

using System;
using Microsoft.Office.Interop.Excel;

class Program
{
  static void Main(string[] args)
  {
    var excelApp = new Application();
    excelApp.Visible = true;
    excelApp.Workbooks.Add(Type.Missing);

    object workSheet = excelApp.ActiveSheet;
    ((Range)((_Worksheet)workSheet).Cells[1, 1]).Value2
                                         = "Hello Office World!";
  }
}
リスト3(Visual Studio 2008の場合)

 C#からExcelを操作し、ワークブックを作成し、「A1」のセルに文字列を入れるというただそれだけの例である。「Type.Missing」は引数の省略機能でなくすことができるので重要な問題ではない。

 それよりもここで問題なのは、キャストがやたら多いことである。その理由の1つが、ActiveSheetプロパティの値をobject型で受けていることである。これはActiveSheetプロパティがそもそもobject型であるという理由による。そのため、どうしても本来の型に戻すキャストを避けて通れない。

 ところが、C# 4になると、このキャストは追放できてしまう(リスト4)。

using Microsoft.Office.Interop.Excel;

class Program
{
  static void Main(string[] args)
  {
    var excelApp = new Application();
    excelApp.Visible = true;
    excelApp.Workbooks.Add();

    dynamic workSheet = excelApp.ActiveSheet;
    workSheet.Cells[1, 1].Value = "Hello Office World!";
  }
}
リスト4

 実は、ActiveSheetプロパティが返す型がobject型からdynamic型に変更されているためだ。dynamic型で受けておけば、その後でキャストを入れることなく、そこからメソッドやプロパティを使用できてしまう。つまり、名前の解決が実行時まで遅延されて動的に行われるため、コンパイル時に名前が確定していなくても構わないわけである。

 さて、ここで以下のような疑問を持った読者も多いと思う。

  • 仕様変更の害を語った直後に、仕様変更を持ち出して「仕様変更によりソース・コードを簡単に記述できる」と褒めるのは矛盾ではないか?

 至極まっとうな疑問だと思うので、ストレートに答えておこう。

 では、なぜ仕様変更は「悪」として排斥されたのだろうか。それは、利用するプログラムの書き換えも要求してしまうからである。

 ならば、この事実はどうだろうか?

 実はVisual Studio 2008用のソース・コードをそのままVisual Studio 2010に持ち込んでも動くのである。なぜ動くのかはじっくり検討すると明らかだ。なぜなら、dynamic型をobject型に変換することは暗黙のうちに可能であり、問題を起こすわけではない。また、本来の型に戻すキャストは、もともとそういう型の値が入っているのだから成功して当たり前。つまり、これは「ソース・コードの互換性を維持したうえでの仕様変更」なのである。

 しかし、「バイナリ・コードが変わってしまえば、ソース・コード互換に意味はない」という意見もあるだろう。だが、これも基本的に問題ない。基本的に.NETのプログラムはサイド・バイ・サイドで動作するので、C# 3.0用にビルドしたバイナリ・コードは、C# 3.0用の.NET Framework基本ライブラリで動き、C# 4用のビルドしたバイナリ・コードはC# 4用の基本ライブラリで動くからだ。しかも、これは「相互運用性レイヤ」の話であって、Officeそのものに変更が入ったわけではない。

名前付きの引数

 Officeが公開するメソッドの特徴の1つは、過剰すぎるともいえる引数の多さにある。たいてい、それだけの数の引数は必要がないが、誰かが必要としている機能を引数として指定できるようにしておくことはやむを得ない。ソフトの機能の肥大化とは、不特定多数の利用者が、異なるリクエストを開発者に投げることで起こる惨劇である。

 よく、「必要のない機能ばかりごてごてと付けて……」とソフトを批判する人が多いが、たいていは開発者も本当は付けたくないのである。ほかの別の誰かが「ぜひ付けてくれ」と要求してソフトは肥大化していくのである。だから、批判を開発者に向けるのは筋違いで、互換性のない異なる意図の要求を野放図に出すほかのエンド・ユーザーたちを本当は批判すべきなのだろうが、そういう例をほとんど筆者は知らない。

 さて、Excelのファイルを開くOpenメソッドも引数が多い。引数は省略可能になったからそれでいい、ともいい切れない。なぜなら、後ろの方の引数だけを指定したくても、途中の引数は省略できないからだ。例えば、Openメソッドは最後の引数で「壊れたファイルを修復する方法」を指定するが、これを指定するとかなり悲惨なコードになる。

using Microsoft.Office.Interop.Excel;

class Program
{
  static void Main(string[] args)
  {
    object missing = System.Reflection.Missing.Value;

    var excelApp = new Application();
    excelApp.Visible = true;
    excelApp.Workbooks.Open("s:\\delme.xlsx",
      missing,
      missing,
      missing,
      missing,
      missing,
      missing,
      missing,
      missing,
      missing,
      missing,
      missing,
      missing,
      missing,
      XlCorruptLoad.xlRepairFile);
  }
}
リスト5

 ちなみに、「XlCorruptLoad.xlRepairFile」は開く際にファイルを修復せよ、という指定である。

 このような場合は、名前で引数を指定するとよい。名前を指定された引数は順番から解放されるので、それより手前の引数をすべて書くことも要求されない。

using Microsoft.Office.Interop.Excel;

class Program
{
  static void Main(string[] args)
  {
    var excelApp = new Application();
    excelApp.Visible = true;
    excelApp.Workbooks.Open(
      "s:\\delme.xlsx", CorruptLoad: XlCorruptLoad.xlRepairFile);
  }
}
リスト6

 たったこれだけである。

 通常、引数の名前を指定すると名前の分だけ長くなって不利だが、このようなケースでは長さが大幅に短くなる。特に使わない引数が多いOffice連携の際は、使ってみてもよい機能だろう。

補足:64bitは問題にならない

 Visual Studio 2010は32bit用、64bit用のIL(中間言語)コードをどちらも作成できる。また、Officeにはすでに32bit版と64bit版が存在する。このような環境を想定すると、すぐに「ビット数を合わせないとうまく動かないのではないか」と思う場合もあるだろう。特にネイティブ・コードのモジュールでは深刻な問題になる。ビット数が異なる.DLLファイルは存在しても読み込めず、リンクできないのである。

 しかし、Officeに関してそのような懸念は必要がない。なぜなら、別のプロセス空間で動作するプロセスのビット数が何であろうと関係ないからだ。あくまで、COMのマーシャリング機能がプロセス境界を越えて(必要に応じて変換しつつ)データを受け渡すだけである。例えば、Visual Studioでプロジェクトのプロパティを開き、[ビルド]タブの「プラットフォーム ターゲット」を「x86」と「x64」の間で切り替えながら実行してみるとよく分かるだろう。どちらでコンパイルしても問題なく動く。


 INDEX
  C# 4入門
  第5回 Office連携でrefキーワード不要
    1.Wordの悪夢/なぜこんなことに?
  2.refキーワードの省略と引数の省略/dynamic型の価値/名前付きの引数
    3.良いOfficeと悪いOffice/まとめ
 
インデックス・ページヘ  「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 記事ランキング

本日 月間