私はC ++の男ではありませんが、これについて考えることを余儀なくされています。C ++では多重継承が可能ですが、C#ではできないのはなぜですか?(ダイヤモンドの問題を知っていますが、それは私がここで尋ねていることではありません)。C ++は、複数の基本クラスから継承された同一のメソッドシグネチャのあいまいさをどのように解決しますか?そして、なぜ同じデザインがC#に組み込まれていないのですか?
私はC ++の男ではありませんが、これについて考えることを余儀なくされています。C ++では多重継承が可能ですが、C#ではできないのはなぜですか?(ダイヤモンドの問題を知っていますが、それは私がここで尋ねていることではありません)。C ++は、複数の基本クラスから継承された同一のメソッドシグネチャのあいまいさをどのように解決しますか?そして、なぜ同じデザインがC#に組み込まれていないのですか?
回答:
C ++では多重継承が可能ですが、C#ではできないのはなぜですか?
Javaでは、言語の表現力を制限して言語を習得しやすくしたいと考えています。なぜなら、多重継承を使用するコードは、多くの場合、それ自体の利益よりも複雑すぎるからです。また、完全な多重継承の実装ははるかに複雑であるため、仮想マシンも大幅に簡素化されました(多重継承は、オブジェクトの中央(ベースの先頭)にポインターを保持する必要があるため、ガベージコレクターと特に相互作用します) )
そして、C#を設計するとき、Javaを見て、完全な多重継承が実際にあまり見逃されていないことを見て、物事を単純にすることを選択したと思います。
C ++は、複数の基本クラスから継承された同一のメソッドシグネチャのあいまいさをどのように解決しますか?
しません。特定のベースから明示的にベースクラスメソッドを呼び出す構文がありますが、仮想メソッドの1つだけをオーバーライドする方法はありません。サブクラスでメソッドをオーバーライドしない場合、ベースを指定せずに呼び出すことはできません。クラス。
そして、なぜ同じデザインがC#に組み込まれていないのですか?
組み込むものは何もありません。
Giorgioがコメントでインターフェイス拡張メソッドに言及しているので、ミックスインとは何か、それらがさまざまな言語でどのように実装されているかを説明します。
JavaおよびC#のインターフェイスは、メソッドの宣言のみに制限されています。ただし、インターフェイスを継承する各クラスにメソッドを実装する必要があります。ただし、一部のメソッドのデフォルトの実装を他のメソッドの観点から提供すると便利なインターフェイスの大きなクラスがあります。一般的な例は同等です(擬似言語):
mixin IComparable {
public bool operator<(IComparable r) = 0;
public bool operator>(IComparable r) { return r < this; }
public bool operator<=(IComparable r) { return !(r < this); }
public bool operator>=(IComparable r) { return !(r > this); }
public bool operator==(IComparable r) { return !(r < this) && !(r > this); }
public bool operator!=(IComparable r) { return r < this || r > this; }
};
完全なクラスとの違いは、これにデータメンバーを含めることができないことです。これを実装するにはいくつかのオプションがあります。明らかに、多重継承は1つです。ただし、多重継承の実装はかなり複雑です。ただし、ここでは必要ありません。代わりに、多くの言語では、クラス自体に挿入されるか、中間ベースクラスが生成されてそこに配置される、クラスとメソッド実装のリポジトリによって実装されるインターフェイスでミックスインを分割することでこれを実装します。これはRubyとDで実装され、Java 8で実装され、不思議な繰り返しテンプレートパターンを使用してC ++で手動で実装できます。上記のCRTP形式では、次のようになります。
template <typename Derived>
class IComparable {
const Derived &_d() const { return static_cast<const Derived &>(*this); }
public:
bool operator>(const IComparable &r) const { r._d() < _d(); }
bool operator<=(const IComparable &r) const { !(r._d() < _d(); }
...
};
そして次のように使用されます:
class Concrete : public IComparable<Concrete> { ... };
これは、通常の基本クラスのように仮想として宣言する必要はありません。そのため、テンプレートでインターフェイスを使用する場合、有用な最適化オプションを開いたままにします。C ++では、これはおそらく2番目の親として継承されますが、複数の継承を許可しない言語では、単一の継承チェーンに挿入されることに注意してください。
template <typename Derived, typename Base>
class IComparable : public Base { ... };
class Concrete : public IComparable<Concrete, Base> { ... };
コンパイラの実装は、仮想ディスパッチを回避する場合と回避しない場合があります。
C#では別の実装が選択されました。C#では、実装は完全に独立したクラスの静的メソッドであり、指定された名前のメソッドは存在しないが、「拡張メソッド」が定義されている場合、メソッド呼び出し構文はコンパイラによって適切に解釈されます。これには、すでにコンパイルされたクラスに拡張メソッドを追加できるという利点と、最適化されたバージョンを提供するなど、そのようなメソッドをオーバーライドできないという欠点があります。
答えは、名前空間の衝突が発生した場合、C ++では正しく機能しないということです。参照してくださいこれを。名前空間の衝突を避けるために、ポインタを使用してあらゆる種類の回転を行う必要があります。私はVisual StudioチームのMSで働いていましたが、少なくとも一部は、名前空間の衝突を完全に回避するために委任を開発しました。以前、インターフェイスも多重継承ソリューションの一部であると考えていたと言っていましたが、私は間違っていました。インターフェイスは実に驚くべきものであり、C ++、FWIWで動作するように作成できます。
委任は、特に名前空間の衝突に対処します。5つのクラスに委任できます。5つのクラスすべてが、メソッドをファーストクラスメンバーとしてスコープにエクスポートします。これを見ると外側は多重継承です。