目次

概要

Ver. 4.0

.NET Framework には COM 相互運用機能があって、COM のクラスをあたかも .NET のクラスであるかのように扱うことができます。 ただ、COM が主流だった時代と今とでは大分設計思想に差があって、 .NET 的には不要だけども、COM 相互運用をする上では欲しい機能というのがいくつかあります。

そこで、C# 4.0 では、COM 相互運用用のクラス (RCW(Runtime Callable Wrapper)といいます。.NET ランタイムから COM を呼び出せるようにしたラッパークラス)に対してだけ特別な処理をするようになりました。 COM への特別処理は以下の2点。

  • ref 引数(「引数の参照渡し」参照)に対して、ref キーワードを付けなくても呼び出せるようになった。

  • get_X(index)set_X(index, value)というメソッドに対して、 インデックス付きプロパティ構文(X[index]という書き方)が使えるようになった。

ref 省略

本来、「引数の参照渡しでは、呼び出し側からも参照渡しであることが一目でわかるべき」 というのが C# の流儀なので、ref キーワードの省略はあまりいい構文ではありません。 (なので、通常は「参照渡し」では ref を省略できない。)

ですが、COM の場合、参照渡しにする必要のないようなものにまでやたらと ref が付きまくるので、 やむなく ref キーワードの省略を認めるようです。 (あくまで RCW (COM 相互運用クラス)に対してだけこの機構が働く。)

例えば、悪名高い Word の Document.SaveAs メソッドを見てみましょう。 C# 3.0 までは以下のような書き方をする必要がありました。

var word = new Microsoft.Office.Interop.Word.Application();

object missing = Type.Missing;
object filename = "sample.docx";
word.ActiveDocument.SaveAs2(ref filename,
    ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing);

これが、C# 4.0 なら以下のように書けたりもします。

object missing = Type.Missing;
string filename = "sample.docx";
word.ActiveDocument.SaveAs2(filename,
    missing, missing, missing, missing,
    missing, missing, missing, missing,
    missing, missing, missing, missing,
    missing, missing, missing, missing);

まあ、この場合、ref 省略よりも、オプション引数(「オプション引数・名前付き引数」参照)が追加されたことの方がインパクトが大きいですが↓。

string filename = "sample.docx";
word.ActiveDocument.SaveAs2(filename);

ただし、これも COM 特別処理のおかげです。 C# 的には本来、ref 引数に対して規定値は定義できないんですが、 RCW の場合には ref がついてても規定値が設定されるようになっています。

インデックス付きプロパティ

C# は「インデックス付きプロパティじゃなくて、インデクサー持ちの型のプロパティを作れ」という設計思想です。 (あるいは、「イテレーター」を使って IEnumerable を返すか。) でも、COM の時代にはそういう思想がなくて、インデックス付きプロパティだらけなので、これもやむなく認めるようになりました。

例えば、Excel の Application.Range がインデックス付きプロパティになっています。 C# 3.0 までは、以下のようにアクセスする必要がありました。

var excel = new Microsoft.Office.Interop.Excel.Application();
var range = excel.get_Range("A1", "A2");

これが、C# 4.0 では以下のように書けます。

var excel = new Microsoft.Office.Interop.Excel.Application();
var range = excel.Range["A1", "A2"];

また、対 COM 限定で、インデクサーやインデックス付きプロパティに対する引数の省略(「オプション引数」参照)が可能です。 すなわち、以下のような記述が許されます。

obj[];

これらの処理は本当に対 RCW (COM 相互運用)専用です。 C# でインデックス付きプロパティが定義できるようになるわけではないです。

それどころか、VB.NET で作ったインデックス付きプロパティにすら、 C# からはインデックス付きプロパティ構文でアクセスできません。 (今まで通り get_X という書き方をする必要があります。) 例えば、VB で以下のように書いたとしても、

Public Class Class1

    Dim x_ As Dictionary(Of String, Integer)

    Public Property X(ByVal i As String) As Integer
        Get
            Return x_(i)
        End Get
        Set(ByVal value As Integer)
            x_(i) = value
        End Set
    End Property

End Class

C# 側からは get_X でしか参照できません。(下図参照。)

C# から見たインデックス付きプロパティ
C# から見たインデックス付きプロパティ

No PIA

C# の機能ではなく、.NET Framework 4 の新機能ですが、No PIA と呼ばれる機能も追加されました。

詳しくは「プラットフォーム呼び出し」で説明します。

更新履歴

ブログ