質問者
抽象クラスでそれを継承するクラスに特定のインターフェースの実装を(通常のインターフェースと同様に)強要させる方法

質問
-
例えば、System.Collections.ICollection は System.Collections.IEnumerable を継承しています。そのため、ICollection を実装するクラス(例えば TheCollection)は IEnumerable も実装する必要があると思うのですが、これは見方を変えると、「ICollection はそれを実装するクラスに IEnumerable インターフェースの実装も強要している」と見なせると思います。
ここで、上記 ICollection の立場で 抽象クラス(例えば ACollection)を作って、それを継承するクラス(TheCollection)に IEnumerable の実装も強要したいと思ったのですがうまく行きません。
仮に、
public abstract class ACollection : IEnumerable {.. }
public class TheCollection : ACollection {.. }
とすると、ACollection に IEnumerable の実装が無いとコンパイルエラーとなってしまい、TheCollection に (ACollection の立場で) IEnumerable の実装を強要できません。
それで、ACollection にて 以下の様に
abstract class ACollection : IEnumerable
{
protected abstract IEnumerator GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
IEnumerable 「メンバの明示的実装」を行い同名の抽象メソッドを改めて定義してやると、TheCollection に GetEnumerator() の実装は強要できますが、これは次の点で不満です。
1. ACollection クラスで、強制させたいインターフェースのメンバ毎に上記コードを記述する必要がある。
2. TheCollection では ACollection 「メンバの明示的実装」を行う事ができない。
C# では、上記 1, 2 の問題が無く、最初に記述した ICollection と同じように その派生クラスに特定のインターフェースを(通常のインターフェースと同様に「メンバの明示的実装」も可能な形で)強制する抽象クラスは、記述できないのでしょうか?
すべての返信
-
kaZm さんからの引用 例えば、System.Collections.ICollection は
仮に、
public abstract class ACollection : IEnumerable {.. }
public class TheCollection : ACollection {.. }
とすると、ACollection に IEnumerable の実装が無いとコンパイルエラーとなってしまい、TheCollection に (ACollection の立場で) IEnumerable の実装を強要できません。そうですね、C# ではクラスはインターフェスを実装することを宣言した場合、そのインターフェスを完全に実装していることを保障しなければならないため、そのようになってしまいます。kaZm さんからの引用 それで、ACollection にて 以下の様に
abstract class ACollection : IEnumerable
{
protected abstract IEnumerator GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
IEnumerable 「メンバの明示的実装」を行い同名の抽象メソッドを改めて定義してやると、TheCollection に GetEnumerator() の実装は強要できますが、私は、あえて上記のように記述することで、抽象クラスとしての役割が明確に定義できるので、問題ないと思います。抽象クラスが派生クラスすべてに対して特定のインターフェスの実装を求めるのであれば、それをすべて abstract で派生クラスにたいして公開するべきですし、抽象クラスの役割としては、もっとコンシューマよりで考えて、いくつかのインターフェス内のメソッドを実装したり、その実装をたすけるメソッドを持つなどを考えるとよいと思います。kaZm さんからの引用 これは次の点で不満です。
1. ACollection クラスで、強制させたいインターフェースのメンバ毎に上記コードを記述する必要がある。
2. TheCollection では ACollection 「メンバの明示的実装」を行う事ができない。上に書いたように、私は抽象クラスは「1」のようにあるべきだと思います。インターフェスの各メソッドごとに、public abstract であったり protected virtual であったり protected abstract で上記のようなメソッドを用意し、派生クラス側でインターフェスがどのように実装されることを想定してるかをはっきりするべきでしょう。また「2」については、C# では特定のインターフェスの再実装を実施できますので、
public class TheCollection : ACollection, IEnumerable{IEnumerator IEnumerable.GetEnumerator(){/* IEnumerable.GetEnumerator() を完全に再実装する */}}
として、ACollection で定義された実装を置き換えることはできます。 -
Hongliang さんからの引用 そもそも、インターフェイスメンバの明示的実装という機能は、インターフェイスメンバをプライベートメンバにするためにあるのではありません。実装する複数インターフェイス間のメンバ名の衝突を避けるための手段です。
この意見には、ちょっと反対です。シグネチャの重複を避けるために明示的実装を利用するというのは、まさにその通りだと思いますが、それはあくまで重なる2つのシグネチャをどのように実装するかという手法でしかなくって、クラスの設計としては「インターフェスに定義されたメソッドを public に公開するか private に明示的実装を行うか」というのは、完全に別の問題として考えるべきだと思います。たとえば、コレクション系でよくあることですが、void Add(object o);void Add(int n);という重複しない2つのメソッドがインターフェスに定義されていて、コレクションに対する操作としてこの2つが同一である場合、整数配列クラスが void Add(object o) を public に実装するのはあまり良い選択とは思えません。汎用的コレクションとして動作するための条件として void Add(object o) の実装を必要とする一方で、整数配列クラスとしての設計としてはこのメソッドは不要であるはずで、このような場合に明示的実装を利用することができます。個人的には明示的実装は現在のような表記じゃなくて、コンパイラサービスネームスペースあたりの属性ベースによる指定であってほしかったと思います。そうすりゃ自身で呼び出すときに手軽だとか、protected なメソッドにも設定できるとか、イロイロと(以下略)