2009年05月05日
川俣晶の縁側ソフトウェア技術雑記total 18119 count

C#のインターフェースの明示的な実装のまとめ

Written By: 川俣 晶連絡先

 そーなのかーを読んで、自分でも曖昧になっていた部分があるので、「C#のインターフェースの明示的な実装」について、自分用の簡単なまとめを書こうと思ったところ、予想以上の混迷を見つけてしまいました。

インターフェースの明示的な実装とは何か §

 インターフェースIFooのメソッド、プロパティ、イベント、インデクサを実装する際に、"その名前"で実装する方法と、"IFoo.その名前"で実装する方法があります。後者が明示的な実装です。

明示的な実装を行うと何が変るのか §

  • クラスに異なるインターフェースを実装する際、それらの間に同名のメソッド、プロパティ、イベント、インデクサがあって安全に区別して実装できる
  • "IFoo.その名前"を経由して外部からアクセスすることはできない。インテリセンスを発動しても表示されない
  • つまり、あたかもprivateであるかのように見える
  • しかし、IFooにキャストしてやると"その名前"経由でアクセス可能になる
  • つまり、あたかもpublicであるかのように見える

疑問 §

 さてここで素朴な疑問です。

 キャストするとprivateがpublicに変化したように見えるとは、いったいどういう意味?

 C#言語仕様では以下のように書かれています。

インターフェイス メンバの明示的実装には、他のメンバとは異なるアクセシビリティ特性があります。インターフェイス メンバの明示的実装は、メソッドの呼び出しやプロパティ アクセスにおいて、完全限定名ではアクセスできません。このため、アクセスはある意味ではプライベートです。ただし、インターフェイス インスタンスからはアクセスできるため、アクセスはある意味でパブリックでもあります。

 ここでは、「完全限定名ではアクセスできない」ことがprivateに見える根拠になっているようにも読めます。ということは、本当はpublicなのに、アクセスできる名前が無いためにprivateであるかのように見えるだけ?

 こういう時は論よりソース。MSのC# 3.0コンパイラの実装はどうなっているのでしょうか? リフレクションで強引にメソッドの情報を読み取れば分かるはずです。

サンプルソース §

using System;

using System.Reflection;

class MyClass : IDisposable

{

    void IDisposable.Dispose() { }

}

class Program

{

    private static void dumpType(Type t)

    {

        MethodInfo[] infos = t.GetMethods(BindingFlags.Public 

            | BindingFlags.NonPublic | BindingFlags.Instance);

        Console.WriteLine("Type Name:{0}", t.Name);

        foreach (var info in infos)

        {

            if (info.Name.Contains("Dispose"))

                Console.WriteLine("{0} is {1}", 

                    info.Name, 

                    info.IsPrivate ? "private" : "non-private");

        }

    }

    static void Main(string[] args)

    {

        dumpType(typeof(MyClass));

        dumpType(typeof(IDisposable));

        MyClass a = new MyClass();

        IDisposable ia = a;

        dumpType(a.GetType());

        dumpType(ia.GetType());

        ia.Dispose();

    }

}

実行結果 §

Type Name:MyClass

System.IDisposable.Dispose is private

Type Name:IDisposable

Dispose is non-private

Type Name:MyClass

System.IDisposable.Dispose is private

Type Name:MyClass

System.IDisposable.Dispose is private

結論と混迷 §

 というわけで、結論が出ました。

 インターフェース本体のメンバはpublicですが、明示的に実装されたインターフェースメンバはprivateです。従って、インターフェース本体の型を経由したメソッドの呼び出しは成立しますが、インターフェースを実装した型を経由した外部からのメソッドの呼び出しは成立しません。

 ……という話で終われば良かったのですが。

 よく見ると上記のサンプルソースの結果にはねじれた状況があります。

 IDisposable ia = a;として宣言されているiaに対して、dumpType(ia.GetType());を実行した結果は以下の通りだからです。

  • "System.IDisposable.Dispose is private"となり、このメソッドはprivateだと主張している
  • "Dispose"というメンバは発見できていない

 それにも関わらず、次行の"ia.Dispose();"というメソッド呼び出しは成立してしまいます。

 コンパイル時に静的に解決される型情報と、実行時に動的に参照される型情報が食い違っているわけで、なかなか悩ましい混迷です。

あるいは別の結論 §

 dumpType(ia.GetType().GetInterface("System.IDisposable"));と書けば、コンパイル時に認識されて呼び出される(と思われる)メソッドの情報を実行時に取得できます。つまり、「インターフェースの明示的な実装」を行った型に対して、呼び出せる可能性のある全てのメソッドを列挙するには、GetMethodsでメソッドの列挙を行うだけでは不十分であり、GetInterfacesメソッドでインターフェースを列挙した上でそれらに対してもGetMethodsでメソッドの列挙を行う必要がある?

Facebook

キーワード【 川俣晶の縁側ソフトウェア技術雑記
【技術雑記】の次のコンテンツ
2009年
05月
14日
メモリの無駄遣いは、具体的にどの程度実行速度をスローダウンさせるか?
3days 0 count
total 3610 count
【技術雑記】の前のコンテンツ
2009年
04月
30日
DELL Precision 380のCPUをCore2に換装できるかについてのメモ
3days 0 count
total 19085 count

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

http://mag.autumn.org/tb.aspx/20090505105325
サイトの表紙【技術雑記】の表紙【技術雑記】のコンテンツ全リスト 【技術雑記】の入手全リスト 【技術雑記】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.36 (Alpha-Test) Copyright (c) 2004-2021 Pie Dey.Co.,Ltd.