インターフェースを使用する理由と一般的に制約されたタイプを使用する理由は何ですか


15

ジェネリック型パラメーター(クラステンプレート、およびパラメーターポリモーフィズムとも呼ばれますが、それぞれの名前には異なる意味があります)をサポートするオブジェクト指向言語では、多くの場合、型パラメーターに型制約を指定して、それを降順にすることができます別のタイプから。たとえば、これはC#の構文です。

//for classes:
class ExampleClass<T> where T : I1 {

}
//for methods:
S ExampleMethod<S>(S value) where S : I2 {
        ...
}

これらのインターフェースによって制約されるタイプよりも実際のインターフェースタイプを使用する理由は何ですか?たとえば、メソッドシグネチャを作成する理由は何I2 ExampleMethod(I2 value)ですか?


4
クラステンプレート(C ++)は、まったく異なるジェネリックよりもはるかに強力なものです。ジェネリックを含む言語は、テンプレート構文を借用していましたが。
デデュプリケーター

インターフェイスメソッドは間接呼び出しですが、タイプメソッドは直接呼び出しにできます。したがって、後者は前者よりも高速であり、ref値型パラメーターの場合、実際に値型を変更する可能性があります。
user541686

@Deduplicator:ジェネリックがテンプレートよりも古いことを考慮すると、ジェネリックがテンプレートから構文やその他のものを借りることができなかったことを確認できません。
ヨルグWミットタグ

3
@JörgWMittag:「ジェネリックをサポートするオブジェクト指向言語」によって、Deduplicatorは「MLとAda」ではなく「JavaとC#」を理解したのではないかと思います。前者に対するC ++の影響は明らかです。ただし、すべての言語がC ++から借用したジェネリックまたはパラメトリック多型を持っているわけではありません。
スティーブジェソップ

2
@SteveJessop:ML、Ada、Eiffel、HaskellはC ++テンプレートより前に登場し、Scala、F#、OCamlは後に登場しましたが、C ++の構文を共有するものはありません。(興味深いことに、C ++、特にテンプレートから大きく借用しているDでさえ、C ++の構文を共有しません。)「JavaとC#」は、「ジェネリックを持つ言語」のかなり狭い視野です。
ヨルグWミットタグ

回答:


21

パラメトリックバージョンを使用すると、

  1. 関数のユーザーへの詳細情報
  2. 作成できるプログラムの数を制限します(無料のバグチェック)

ランダムな例として、二次方程式の根を計算する方法があるとします

int solve(int a, int b, int c) {
  // My 7th grade math teacher is laughing somewhere
}

そして、あなたはそれ以外のもののような他の種類の数でそれが働いて欲しいですint。次のように書くことができます

Num solve(Num a, Num b, Num c){
  ...
}

問題は、これはあなたがそれを望んでいることを言っていないということです。それは言う

数字のようなものを3つ(必ずしも同じ方法である必要はありません)教えてください。何らかの数字を返します。

私たちは次のような何かを行うことができないint sol = solve(a, b, c)場合はab、とcしているint私たちは、メソッドが返すために起こっていることを知らないので、Sをint最後に!これは、より大きな表現でソリューションを使用したい場合、ダウンキャスティングと祈りを伴ういくつかの厄介なダンスにつながります。

関数内で、誰かがfloat、bigint、degreeを渡してくれる可能性があり、それらを加算および乗算する必要があります。これらの3つのクラス間の操作は意味をなさないため、静的にこれを拒否します。度はmod 360であるため、そうではなくa.plus(b) = b.plus(a)、同様の喜びが生じるでしょう。

サブタイプ付きのパラメータポリモーフィズムを使用する場合、このタイプは実際に私たちが意味することを言うので、これをすべて除外できます

<T : Num> T solve(T a, T b, T c)

または、「数であるタイプを教えてくれれば、それらの係数を使って方程式を解くことができます」。

これは、他の多くの場所でも発生します。例のもう一つの良いソースは、コンテナ、ALAのいくつかの並べ替えの上に抽象的機能でありreversesortmapなど、


8
要約すると、汎用バージョンは、3つの入力(および出力)がすべて同じタイプの数値になることを保証します。
MathematicalOrchid

ただし、問題のタイプを制御しないと、これは不十分になります(したがって、インターフェイスを追加できません)。最大の一般性を得るにはNum<int>、追加の引数として、引数の型(例:)でパラメータ化されたインターフェイスを受け入れる必要があります。委任により、あらゆるタイプのインターフェイスをいつでも実装できます。これは基本的にHaskellの型クラスです。ただし、インターフェイスを明示的に渡す必要があるため、使用するのがはるかに面倒です。
ドーバル

16

これらのインターフェイスによって制約されるタイプではなく、実際のインターフェイスタイプを使用する理由は何ですか?

それがあなたが必要なものだから...

IFoo Fn(IFoo x);
T Fn<T>(T x) where T: IFoo;

2つの明らかに異なる署名です。最初は、インターフェイスを実装する任意の型を取りそれが行う唯一の保証は、戻り値がインターフェイスを満たすことです。

2番目は、インターフェイスを実装する任意の型を取り、少なくとも制限のないインターフェイスを満たすものではなく、少なくともその型を再度返すことを保証します。

時には、より弱い保証が必要な場合があります。時には、より強いものが必要です。


弱い保証バージョンをどのように使用するか例を示していただけますか?
グレッグロス

4
@GregRos-たとえば、私が書いたいくつかのパーサーコード。Or2つのParserオブジェクト(抽象基本クラスですが、原則は保持されます)を取得し、新しいParser(ただし、異なるタイプの)オブジェクトを返す関数があります。エンドユーザーは、具体的なタイプが何であるかを知らないか、気にしないでください。
Telastyn

C#では、渡されたT以外のTを返すことは、新しい制約なしではほとんど不可能(反射の痛みなし)であり、強力な保証はそれ自体ではほとんど役に立たないと思います。
NtscCobalt

1
@NtscCobalt:パラメトリックプログラミングとインターフェイスジェネリックプログラミングの両方を組み合わせるとより便利です。LINQはすべての時間を何例(受け入れIEnumerable<T>、別返しIEnumerable<T>実際に例えばされているOrderedEnumerable<T>
ベン・フォークト

2

メソッドのパラメーターに制約付きジェネリックを使用すると、渡されたものに基づいてメソッドの戻り値の型を決定できます。.NETでは、追加の利点もあります。その中で:

  1. 制約付きジェネリックをrefor outパラメータとして受け入れるメソッドには、制約を満たす変数を渡すことができます。対照的に、インターフェイスタイプパラメータを持つ非ジェネリックメソッドは、そのインターフェイスタイプの変数を受け入れることに制限されます。

  2. ジェネリック型パラメーターTを持つメソッドは、Tのジェネリックコレクションを受け入れるIList<T> where T:IAnimalことができます。を受け入れるメソッドはを受け入れることができますList<SiameseCat>が、望んでいたメソッドは受け入れるIList<Animal>ことができません。

  3. 制約では、ジェネリック型などでインターフェイスを指定できる場合がありますwhere T:IComparable<T>

  4. インターフェースを実装する構造体は、制約付きジェネリックパラメーターを受け入れるメソッドに渡されるときに値型として保持されますが、インターフェース型として渡される場合はボックス化する必要があります。これは速度に大きな影響を与えます。

  5. ジェネリックパラメーターには複数の制約を設定できますが、「IFooとIBarの両方を実装するタイプ」のパラメーターを指定する他の方法はありません。型のパラメーターを受け取ったコードはIFoo、問題のインスタンスがすべての制約を満たしている場合でも、二重制約のジェネリックを期待するようなメソッドに渡すのが非常に難しいため、これは両刃の剣であることがあります。

特定の状況でジェネリックを使用してもメリットがない場合は、インターフェイスタイプのパラメーターをそのまま受け入れます。ジェネリックを使用すると、型システムとJITterに追加の作業が強制されるため、メリットがない場合は実行しないでください。一方、上記の利点の少なくとも1つが適用されることは非常に一般的です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.