none
コレクションを作成するときに継承するインターフェースについて。 RRS feed

  • 質問

  • IList<T> を継承して独自のコレクションを作成しました。
    IList<T> は、ICollection<T> と IEnumerable<T> を継承しているため
    ICollection<T> と IEnumerable<T> のメンバも必然的に実装することになります。
    特に問題もなく動作しており完全に要求は満たしているのでよいのですが、
    同じようなコレクションである DataGridViewRowCollection を見て疑問に思いました。
    なぜか、IList だけではなく明示的に ICollection と IEnumerable も継承しているのです。
    .NET Framwork に含まれる他のコレクションも見てみましたが
    同じように重複して (?) 継承してるものがたくさん見つかりました。

    これはどういう意味があってこのような継承をしているのでしょうか?
    何か意味があったり、そもそも作法として全てセットで継承すべきであれば
    自作のコレクションも同じように修正しなければと思った次第です。

    2006年9月9日 7:59

回答

  •  proxyreg さんからの引用

    しかし、なぜか継承リストで直接継承しなくても明示的実装ができてしまいます。

    は、確かに。恥ずかしい勘違いでした。すいません。

    んー、となると明示的実装しているという宣言以上のものは無いような気がしてきました。基底クラスがあってそのインターフェイスの実装を再定義する必要があるってんなら話は別なんですが、List<T> とかは別に基底クラスがある訳じゃないですしねぇ。

    2006年9月9日 12:28
  • C#言語仕様 Version1.2 (通常、C:\Program Files\Microsoft Visual Studio 8\VC#\Specifications\1041にある)の310ページの説明によれば、

    interface Ihoge

    interface Ifuga : Ihoge

    interface Imoge : Ihoge

    class hoge : Ihoge, Ifuga, Imoge

    の場合、クラスhogeは、単にIhoge, Ifuga, Imogeの3つのインターフェースを実装すると見なされるとあります。

    つまり、今回のケースは、単に人間がわかりやすいように、インターフェースが並べて書いてあるだけじゃないでしょうか?

    2006年9月9日 16:17
    モデレータ

すべての返信

  • メンバを明示的に実装するためでしょう。C# においては(CRL 的にどうかは知りませんが)明示的実装を行う際、メンバを宣言するインターフェイスを直接実装する必要があります。

    なぜ明示的実装を行うか、については、同名異返値のメンバを定義したかったり、公開すべきでないメンバを直接 public でないようにしたり、と言った辺りが理由になります。MSDN にもガイドラインが載っています。

    2006年9月9日 9:47
  • 確かに言われる通り、DataGridViewRowCollection の明示的インターフェイスの実装を見てみると
    ICollection と IEnumerable のメンバが含まれていました。

    しかし、なぜか継承リストで直接継承しなくても明示的実装ができてしまいます。
    わたしが試した方法は、まず IList<T> と ICollection<T> を継承したクラスを作成し
    IList<T> のコンテキスト メニューから暗黙的実装を行います。
    そうすると、IList<T>、ICollection<T>、IEnumerable<T> と IEnumerable のメンバのスタブが作成されます。
    次に、ICollection<T> のメンバをすべて削除します。
    次に、ICollection<T> のコンテキスト メニューから明示的実装を行います。
    最後に、継承リストから ICollection<T> を削除します。
    しかしこの状態でビルドしてもエラーにならないのです。
    もちろん、ICollection<T> へキャストすると ICollection<T> のメンバにもアクセスできます。

    2006年9月9日 10:53
  •  proxyreg さんからの引用

    しかし、なぜか継承リストで直接継承しなくても明示的実装ができてしまいます。

    は、確かに。恥ずかしい勘違いでした。すいません。

    んー、となると明示的実装しているという宣言以上のものは無いような気がしてきました。基底クラスがあってそのインターフェイスの実装を再定義する必要があるってんなら話は別なんですが、List<T> とかは別に基底クラスがある訳じゃないですしねぇ。

    2006年9月9日 12:28
  • C#言語仕様 Version1.2 (通常、C:\Program Files\Microsoft Visual Studio 8\VC#\Specifications\1041にある)の310ページの説明によれば、

    interface Ihoge

    interface Ifuga : Ihoge

    interface Imoge : Ihoge

    class hoge : Ihoge, Ifuga, Imoge

    の場合、クラスhogeは、単にIhoge, Ifuga, Imogeの3つのインターフェースを実装すると見なされるとあります。

    つまり、今回のケースは、単に人間がわかりやすいように、インターフェースが並べて書いてあるだけじゃないでしょうか?

    2006年9月9日 16:17
    モデレータ
  • 使用者がわかりやすいように継承リストに直接追加しているということで理解いたしました。

    Hongliang さん、trapemiya さん
    回答ありがとうございました。

    2006年9月10日 3:28
  • 出先から、まったくなにもみないで類推だけで書きますが、
     
    基底クラスで実装済みのインターフェスメソッドを再実装しているんではないですか?
    # 前にもこのフォーラムの違うツリーで書いたきがする
     
    インターフェスのメソッドやプロパティは、暗黙的に virtual であり、必ず override することができます。しかし、C# の文法の都合上、明示的実装を行われたインターフェスは private であり、非 virtual です。これを override するために、派生クラスの継承リストに目的のインターフェスを再度記載し、メソッドなどを実装しなおすことができます。
     
    2006年9月10日 14:01
  • 私もそうかな~と思ったんですが、Hongliang さんも書かれてますが、List<T>ってどこからも派生してないんで、インターフェースマップを変更してるわけじゃないんだろうな~と思った次第です。
    2006年9月10日 16:13
    モデレータ
  • なるほど、Hongliang さんの記述は見逃していました。
     
    ところで、ちょっと気になったのですが、
     proxyreg さんからの引用
    同じようなコレクションである DataGridViewRowCollection を見て疑問に思いました。
    なぜか、IList だけではなく明示的に ICollection と IEnumerable も継承しているのです。
    にて「DataGridViewRowCollection を見て」とありますが、DataGridViewRowCollection の何を見て判断されたのでしょうか?
    DataGridViewRowCollection って .NET の標準クラスですよね? ソースコードが提供されていないので、簡単には見れないと思うのですが...。
     
    もし、リフレクションを利用してインターフェスマップを参照されたということであれば、実装されているすべてのインターフェスが表示されるのが当然であって、ソースコード上で再実装を宣言しているかどうかは関係ありません。

    public interface A {}
    public interface B : A {}
     
    public class X : A, B {}
    public class Y : B { }

     
    というソースコードから生成されたクラス X, Y  ともに、リフレクションによるインターフェスマップには A, B と記載されます。
    派生クラスでメソッドを再実装した場合であっても、インターフェスマップには何も変更は加えられず、X の派生クラスおよび Y の派生クラスは、追加のインターフェスを実装しないかぎり、すべて A, B というインターフェスマップのリストを持ちます。派生クラスでメソッドを再実装した場合は、各インターフェスのメソッドリファレンスが、再実装したメソッドのジャンパー(JITer と NativeCode を切り替えるコードポイント)に変更されるのみです。
     
    なので、リフレクションで得たインターフェスマップを見て、ソースコード上にそれらのインターフェスが列挙されていると勘違いされているのではないでしょうか? リフレクションで得られる情報は、あくまでメタデータとしてロードされた CLR 上での構造化された情報でしかありませんので、ソースコード上の記載とは無縁だと考えてください。
    2006年9月10日 19:00