質問

次のことを行ってください:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= compile-time error: 
                     // "The 'ref' argument doesn't match the parameter type"
    }

    void Foo(A a) {}

    void Foo2(ref A a) {}  
}

上記のコンパイル時エラーが発生するのはなぜですか?これは、 ref 引数と out 引数の両方で発生します。

役に立ちましたか?

解決

=============

更新:この回答をこのブログエントリの基礎として使用しました:

refパラメータとoutパラメータで型のバリエーションが許可されないのはなぜですか?

この問題の詳細については、ブログページを参照してください。すばらしい質問をありがとう。

=============

クラス Animal Mammal Reptile Giraffe Turtle および Tiger 、明らかなサブクラス化関係。

メソッド void M(ref Mammal m)があるとします。 M は、 m の読み取りと書き込みの両方が可能です。


  

Animal 型の変数を M に渡すことはできますか?

いいえ。その変数には Turtle を含めることができますが、 M は哺乳類のみが含まれると想定します。 Turtle Mammal ではありません。

結論1 ref パラメーターを「大きく」することはできません。 (哺乳類よりも動物の数が多いため、より多くのものを含めることができるため、変数は「大きく」なります。)


  

Giraffe 型の変数を M に渡すことはできますか?

いいえ。 M m に書き込むことができ、 M m Tiger を書き込むことができます。 >。これで、実際には Giraffe 型の変数に Tiger を入れました。

結論2 ref パラメータを「小さく」することはできません。


N(out Mammal n)を検討します。

  

Giraffe 型の変数を N に渡すことはできますか?

いいえ。 N n に書き込むことができ、 N Tiger を書き込むことができます。

結論3 out パラメーターを「小さく」することはできません。


  

Animal 型の変数を N に渡すことはできますか?

うーん。

さて、なぜですか? n n から読み取ることができません。書き込みのみが可能ですよね? Animal 型の変数に Tiger を書き込むと、設定は完了ですよね?

間違っています。ルールは&quot; N n &quot;にしか書き込めません。

ルールの概要は次のとおりです。

1) N は、 N が正常に戻る前に n に書き込む必要があります。 ( N がスローされた場合、すべてのベットはオフになります。)

2) n は、 n から何かを読み取る前に、 n に何かを書き込む必要があります。

これにより、次の一連のイベントが許可されます。

  • Animal 型のフィールド x を宣言します。
  • x への out パラメータとして x を渡します。
  • N n Tiger を書き込みます。これは x のエイリアスです。
  • 別のスレッドで誰かが Turtle x に書き込みます。
  • N n の内容を読み取ろうとし、タイプ Mammalの変数であると思われる Turtle を発見します。

明らかにそれを違法にしたいのです。

結論4 out パラメーターを「大きく」することはできません。


最終的な結論 ref パラメーターと out パラメーターのいずれも、タイプが異なる場合があります。そうでなければ、検証可能な型安全性を破ることです。

これらの基本型理論の問題に興味がある場合は、

他のヒント

どちらの場合も、ref / outパラメーターに値を割り当てることができる必要があるため。

bを参照としてFoo2メソッドに渡し、Foo2でa = new A()を指定しようとすると、これは無効になります。
書くことができない同じ理由:

B b = new A();

共分散(および反分散)の古典的なOOP問題に苦労しています。ウィキペディア:この事実は直感的な期待に反する可能性があるため、基本クラスの代わりに派生クラスを変更可能な(割り当て可能な)引数(およびアイテムが割り当て可能なコンテナ、ちょうど同じ理由) Liskovの原則を引き続き尊重します。なぜそうなのかは、既存の回答でスケッチされており、これらのwiki記事とそこからのリンクでさらに詳しく調べられています。

伝統的に静的に型保証されたままであるように見えるOOP言語は、「不正」です。 (非表示の動的型チェックを挿入するか、チェックするためにすべてのソースのコンパイル時検査が必要です);基本的な選択は、この共分散を放棄して実務者の困惑を受け入れるか(C#がここで行うように)、動的タイピングアプローチに移行する(最初のOOP言語であるSmalltalkが行うように)か、不変に移行する(単一-割り当て)関数型言語が行うようなデータ(不変性の下では、共分散をサポートできます。また、可変データの世界ではSquareサブクラスRectangleを使用できないなど、他の関連するパズルを回避できます)。

検討:

class C : A {}
class B : A {}

void Foo2(ref A a) { a = new C(); } 

B b = null;
Foo2(ref b);

タイプセーフに違反する

他の応答はこの動作の背後にある理由を簡潔に説明していますが、この性質を実際に行う必要がある場合は、Foo2を汎用メソッドにすることで同様の機能を実現できることに言及する価値があると思います:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= no compile error!
    }

    void Foo(A a) {}

    void Foo2<AType> (ref AType a) where AType: A {}  
}

Foo2 ref B を与えると、 Foo2 A B の一部。

コンパイラがオブジェクトを明示的にキャストして、意図が何であるかを確認できるようにしたいと言っているのではありませんか?

Foo2(ref (A)b)

安全性の観点からは理にかなっていますが、参照によって渡される多目的オブジェクトの合法的な使用があるため、コンパイラがエラーではなく警告を出した場合、私はそれを好むでしょう。例:

class Derp : interfaceX
{
   int somevalue=0; //specified that this class contains somevalue by interfaceX
   public Derp(int val)
    {
    somevalue = val;
    }

}


void Foo(ref object obj){
    int result = (interfaceX)obj.somevalue;
    //do stuff to result variable... in my case data access
    obj = Activator.CreateInstance(obj.GetType(), result);
}

main()
{
   Derp x = new Derp();
   Foo(ref Derp);
}

これはコンパイルされませんが、動作しますか?

タイプに実用的な例を使用すると、表示されます:

SqlConnection connection = new SqlConnection();
Foo(ref connection);

これで、祖先 i.e。 Object )を取る関数ができました:

void Foo2(ref Object connection) { }

これで何が問題になる可能性がありますか?

void Foo2(ref Object connection)
{
   connection = new Bitmap();
}

BitConnection SqlConnection に割り当てることができました。

それはダメです。


他のユーザーともう一度やり直してください:

SqlConnection conn = new SqlConnection();
Foo2(ref conn);

void Foo2(ref DbConnection connection)
{
    conn = new OracleConnection();
}

OracleConnection SqlConnection の上に重ねます。

私の場合、私の関数はオブジェクトを受け入れましたが、何も送信できなかったため、単純に送信しました

object bla = myVar;
Foo(ref bla);

そしてそれは動作します

My FooはVB.NETにあり、内部の型をチェックし、多くのロジックを実行します

回答が重複しているが他の回答が長すぎる場合は謝罪します

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top