3

これが機能しないことに気付きました:

var dict = new Dictionary<int, XElement>();
XContainer element;
//...
if (dict.TryGetValue(idx, out element)) { //...

それから私はこれを試しました:

class A { }
class B : A { }

class Program {
    static void Main() {
        A a;
        a = Ret();  // no error, ok
        Ref(ref a); // compiler error, ok...
        Out(out a); // compiler error, lolwut?!
    }
    static B Ret() { return null; }
    static void Ref(ref B b) { }
    static void Out(out B b) { b = null; }
}

最後の呼び出しでコンパイラ エラーが発生するのはなぜですか?

編集:わかりました。答えからわかるように、「out」は変装した「ref」であるため、他の関数やスレッドによって共有および変更できます。しかし、実際には、'out' は関数から複数の値を返す方法ではないでしょうか? もしそうなら、それはそれが得意ではないようです。共有によって問題が生じる場合は、共有しないでください。関数の開始時に隠し変数を作成し、それを操作するだけです。つまり:

static void Out(out B b) {
    B bHidden; // compiler generated;
        // all references to b are replaced with bHidden;
    b = bHidden;
}

このようにできない理由はありますか?これで安全に思えます...

4

4 に答える 4

3

回答からわかるように、「out」は「ref」に変装しているため、他の関数やスレッドで共有および変更できます。しかし、実際には、「out」は関数から複数の値を返す方法ではないでしょうか。もしそうなら、それはそれが得意ではないようです。共有によって問題が発生する場合は、共有しないでください。関数の開始時に非表示の変数を作成し、それを操作するだけです。つまり:

static void Out(out B b) 
{     
    B bHidden; // compiler generated;
               // all references to b are replaced with bHidden;
    b = bHidden; 
} 

このようにできない理由はありますか?今は安全そうです...

このようなシステムは、明らかな理由から「コピーアウト」システムと呼ばれます。それはそのように行うことができますが、そうすることはそれ自体が興味深い問題を引き起こします。例えば:

void M()
{
    int b = 1;
    try
    { 
        N(out b);
    }
    catch (FooException)
    {
        Console.WriteLine(b);
    }
}

void N(out int c)
{
    c = 123;
    P();
    c = 456;
}

void P()
{
    throw new FooException();
}

プログラムの出力は何ですか?それはどうあるべきですか?

次の問題:outの動作をrefと一致させたいですか、それとも矛盾させたいですか?一貫性を欠いたい場合は、おめでとうございます。非常に紛らわしい矛盾を言語に追加しただけです。一貫性を持たせたい場合は、refに「コピーインコピーアウト」セマンティクスを使用させる必要があります。これにより、パフォーマンスと正確性の両方の点で、それ自体に多くの問題が発生します。

参照セマンティクスとコピーセマンティクスの違いを1日中列挙することはできますが、そうはしません。私たちが持っているシステムは私たちが持っているシステムなので、それを使って作業することを学びましょう。

さらに、メソッドから複数の値を返したい場合は、outパラメーターを使用しないでください。それは2001年には賢明なことだったかもしれませんが、今は2012年であり、より多くのツールを自由に使用できます。2つの値を返したい場合:

  • タプルを返す
  • コードを2つのメソッドにリファクタリングし、それぞれが1つの値を返す
  • 2つの値が値型とboolの場合、null許容値型を返します。
于 2012-02-18T15:46:34.353 に答える
2

C#仕様は、型が完全に一致する必要があることを示しています。

17.5.1.3出力パラメータ

仮パラメーターが出力パラメーターである場合、メソッド呼び出しの対応する引数は、キーワードoutと、それに続く仮パラメーターと同じタイプの変数参照(§12.3.3.27)で構成されます。

許可されている場合は、次のようにすることができます。

class A { }
class B : A { public void BOnlyMethod() { } }
class C : A { }

public class Violations
{
    private A a;

    public void DoIt()
    {
        Violate(out this.a);
    }

    void Violate(out B b)
    {
        b = new B();
        InnocentModification();
        // what we think is B, is now C in fact, yet we still can do this:
        b.BOnlyMethod();
        // which is bound to fail, as BOnlyMethod is not present on type C
    }

    void InnocentModification()
    {
        this.a = new C();
    }
}

そのような制限が存在しなかった場合、上記のような型システムの違反は簡単に達成できません。そして、私はあなたがあなたの言語でこの種の「可能性」を望まないと思います。

于 2012-02-18T14:12:41.907 に答える
2

Eric Lippertはこれについて書いています:http://blogs.msdn.com/b/ericlippert/archive/2009/09/21/why-do-ref-and-out-parameters-not-allow-type-variation.aspx

例の変更:

class A { }
class B : A { public int x; }

class Program {
    static void Main() {
        A a;
        a = Ret();
        Out(out a, () => a = new A());
    }
    static B Ret() { return null; }
    static void Ref(ref B b) { }
    static void Out(out B b, Action callback) {
        b = new B();
        callback();
        b.x = 3; // cannot possibly work, b is an a again!
    }
}
于 2012-02-18T14:16:29.307 に答える
1

質問は基本的に次のとおりです。論理的に同等であるa = Ret()と想定されていませんか? Out(out a)もしそうなら、なぜ一方が機能し、もう一方が機能しないのですか?

私の理解が正しければ、CLR は実際には を持っておらずout、代わりに を使用しrefています。つまり、舞台裏Out(out a)では として実装されておりOut(ref a)、明らかな理由で失敗しています。

于 2012-02-18T14:22:34.757 に答える