11

C#仕様では、引数の型を同時に共変と反変の両方にすることはできないと規定されています。

これは、共変または反変のインターフェースを作成するときに、タイプパラメーターをそれぞれ「out」または「in」で装飾するときに明らかです。両方を同時に許可するオプションはありません(「outin」)。

この制限は単に言語固有の制約ですか、それとも圏論に基づいて、タイプを共変と反変の両方にしたくないという、より深く、より根本的な理由がありますか?

編集:

私の理解では、配列は実際には共変と反変の両方でした。

public class Pet{}
public class Cat : Pet{}
public class Siamese : Cat{}
Cat[] cats = new Cat[10];
Pet[] pets = new Pet[10];
Siamese[] siameseCats = new Siamese[10];

//Cat array is covariant
pets = cats; 
//Cat array is also contravariant since it accepts conversions from wider types
cats = siameseCats; 
4

7 に答える 7

25

他の人が言っているように、ジェネリック型が共変と反変の両方であるということは論理的に矛盾しています。これまでのところ、いくつかの優れた答えがありますが、さらに2つ追加します。

まず、分散の「有効性」に関する私の記事を読んでください。

http://blogs.msdn.com/b/ericlippert/archive/2009/12/03/exact-rules-for-variance-validity.aspx

定義上、型が「共変的に有効」である場合、それは反変的な方法で使用できません。「反変的に有効」である場合、共変的に使用することはできません。共変的に有効であり、反変的に有効であるものは、共変または反変のいずれの方法でも使用できませ。つまり、不変です。したがって、共変量と反変量の和集合があります。それらの和集合は不変です

次に、あなたが希望を持っていて、私が望むように機能する型注釈があったと仮定しましょう。

interface IBurger<in and out T> {}

があるとしますIBurger<string>。共変であるため、これはに変換可能IBurger<object>です。IBurger<Exception>これは反変であるため、「文字列」と「例外」に共通点はありませんが、これはに変換可能です。基本的に「インとアウト」とは、任意の2つの参照型T1とT2の任意の型にIBurger<T1>変換できることを意味します。それはどのように役立ちますか?そのような機能で何をします?があるとしますが、オブジェクトは実際にはです。「実際の」型引数はまったく無関係な型であるため、型引数が例外であるという事実を利用し、その型引数を完全に嘘にすることができるので、それで何ができるでしょうか。IBurger<T2>IBurger<Exception>IBurger<string>

フォローアップの質問に答えるには:配列を含む暗黙の参照型変換は共変です; それらは反変ではありません。それらが反変であると誤って信じている理由を説明できますか?

于 2010-12-25T01:42:01.353 に答える
8

共変性と反変性は相互に排他的です。あなたの質問は、セットAがセットBのスーパーセットとセットBのサブセットの両方になることができるかどうかを尋ねるようなものです。セットAがセットBのサブセットとスーパーセットの両方になるには、セットAがセットBと等しくなければなりません。セットAがセットBと等しいかどうかを尋ねるだけです。

言い換えると、同じ引数で共分散と反変性を求めることは、デフォルトである分散をまったく求めないこと(不変)に似ています。したがって、キーワードで指定する必要はありません。

于 2010-12-24T20:35:01.583 に答える
5

共分散は、決して入力しない型に対して可能です(たとえば、メンバー関数はそれを戻り型またはoutパラメーターとして使用できますが、入力パラメーターとしては使用できません)。決して出力しない型(たとえば、入力パラメーターとして、ただし戻り型またはoutパラメーターとしては決して)では、反変性が発生する可能性があります。

共変と反変の両方の型パラメーターを作成した場合、それを入力することも出力することもできませんでした。まったく使用できませんでした。

于 2010-12-24T20:36:43.310 に答える
1

アウトとインのキーワードがなければ、共変性と反変性の議論はありませんか?

inは、引数が関数の引数タイプとしてのみ使用できることを意味します

outは、引数が戻り値型としてのみ使用できることを意味します

inとoutがないということは、引数型および戻り値型として使用できることを意味します

于 2010-12-24T20:31:49.150 に答える
0

この制限は単に言語固有の制約ですか、それとも圏論に基づいて、タイプを共変と反変の両方にしたくないという、より深く、より根本的な理由がありますか?

いいえ、基本的な論理(または常識のどちらか好きな方)に基づいたはるかに単純な理由があります。ステートメントは、同時に真であると同時に真ではないということはできません。

共分散S <: T ⇒ G<S> <: G<T>と反変性はを意味しS <: T ⇒ G<T> <: G<S>ます。これらが同時に真になることは決してないことはかなり明白なはずです。

于 2010-12-25T00:15:15.130 に答える
0

「共変」で何ができる?

Covariantは修飾子outを使用します。つまり、型はメソッドの出力にはなり得ますが、入力パラメーターにはなり得ません。

次のクラスとインターフェイスがあるとします。

interface ICanOutput<out T> { T getAnInstance(); }

class Outputter<T> : ICanOutput<T>
{
    public T getAnInstance() { return someTInstance; }
}

TBigここで、継承するタイプがあるとしますTSmall。これは、TBigインスタンスも常にインスタンスであることを意味しTSmallます。ただし、TSmallインスタンスは必ずしもインスタンスではありませんTBig。(名前は、TSmall内部のフィッティングを視覚化するのが簡単になるように選択されましたTBig

これを行うとき(古典的な共同バリアントの割り当て):

//a real instance that outputs TBig
Outputter<TBig> bigOutputter = new Outputter<TBig>();

//just a view of bigOutputter
ICanOutput<TSmall> smallOutputter = bigOutputter;
  • bigOutputter.getAnInstance()を返しますTBig
  • そしてsmallOutputter割り当てられたのでbigOutputter
    • 内部的に、smallOutputter.getAnInstance()戻りますTBig
    • そしてTBigに変換することができますTSmall
    • 変換が行われ、出力はTSmallです。

それが反対だった場合(それが反対の変種であるかのように):

//a real instance that outputs TSmall
Outputter<TSmall> smallOutputter = new Outputter<TSmall>();

//just a view of smallOutputter
ICanOutput<TBig> bigOutputter = smallOutputter;
  • smallOutputter.getAnInstance()戻りますTSmall
  • そしてbigOutputter割り当てられたのでsmallOutputter
    • 内部的に、bigOutputter.getAnInstance()戻りますTSmall
    • しかし、TSmallに変換することはできませんTBig!!
    • その場合、これは不可能です。

これが、 「コントラバリアント」タイプを出力タイプ として使用できない理由です。


「反変性」で何ができるの?

上記と同じ考え方に従って、反変は修飾子inを使用します。つまり、型はメソッドの入力パラメーターにすることができますが、出力パラメーターにすることはできません。

次のクラスとインターフェイスがあるとします。

interface ICanInput<in T> { bool isInstanceCool(T instance); }

class Analyser<T> : ICanInput<T>
{
    bool isInstanceCool(T instance) { return instance.amICool(); }
}

繰り返しますが、型をTBig継承するとしますTSmall。これは、それTBigが行うすべてのことを実行できることを意味しますTSmall(すべてのTSmallメンバー以上が含まれます)。しかし、TSmallすべてを行うことはできませんTBigTBigより多くのメンバーがいます)。

これを行うと(古典的なコントラバリアントの割り当て):

//a real instance that can use TSmall methods
Analyser<TSmall> smallAnalyser = new Analyser<TSmall>();
    //this means that TSmall implements amICool

//just a view of smallAnalyser
ICanInput<TBig> bigAnalyser = smallAnalyser;
  • smallAnalyser.isInstanceCool
    • smallAnalyser.isInstanceCool(smallInstance)のメソッドを使用できますsmallInstance
    • smallAnalyser.isInstanceCool(bigInstance)メソッドを使用することもできます(のTSmall一部のみを参照していますTBig
  • そして、bigAnalyser割り当てられたのでsmallAnalyer
    • 電話しても大丈夫ですbigAnalyser.isInstanceCool(bigInstance)

それが反対の場合(それが共同バリアントであるかのように):

//a real instance that can use TBig methods
Analyser<TBig> bigAnalyser = new Analyser<TBig>();
    //this means that TBig has amICool, but not necessarily that TSmall has it    

//just a view of bigAnalyser
ICanInput<TSmall> smallAnalyser = bigAnalyser;
  • の場合bigAnalyser.isInstanceCool
    • bigAnalyser.isInstanceCool(bigInstance)のメソッドを使用できますbigInstance
    • しかし、でメソッドをbigAnalyser.isInstanceCool(smallInstance)見つけることができません!!! そして、これが変換されたものでさえあるという保証はありません。 TBigTSmallsmallInstanceTBig
  • そして、smallAnalyser割り当てられたのでbigAnalyser
    • 呼び出しは、インスタンス内のメソッド smallAnalyser.isInstanceCool(smallInstance)を見つけようとしますTBig
    • また、これはインスタンスではない可能性があるTBigため、メソッドが見つからない場合があります。 smallInstanceTBig

これが、 「coバリアント」タイプを入力パラメーターとして使用できない理由です。


両方に参加する

では、2つの「できない」を一緒に追加するとどうなりますか?

  • これはできません+それはできません=何もできません

あなたは何ができますか?

私はこれをテストしていません(まだ...これを行う理由があるかどうかを考えています)が、いくつかの制限があることを知っていれば、問題ないようです。

目的の型のみを出力するメソッドと、それを入力パラメーターとしてのみ受け取るメソッドを明確に分離している場合は、2つのインターフェースを使用してクラスを実装できます。

  • in出力されないメソッドのみを使用および持つ1つのインターフェースT
  • 入力をout受け取らないメソッドのみを持つ別のインターフェースT

必要な状況で各インターフェイスを使用しますが、相互に割り当てようとしないでください。

于 2018-04-19T15:42:01.080 に答える
0

ジェネリック型パラメーターは、共変と反変の両方にすることはできません。

なんで?これは、修飾子が課す制限と関係がありinますout。ジェネリック型パラメーターを共変と反変の両方にしたい場合、基本的に次のようになります。

  • インターフェイスのどのメソッドもTを返しません
  • 私たちのインターフェースのどのメソッドもTを受け入れません

これにより、基本的にジェネリックインターフェイスが非ジェネリックになります。

私は別の質問の下でそれを詳細に説明しました:

于 2018-11-03T20:06:08.673 に答える