2

私は今、厳密に試すことはできませんが、誰かが知っていると確信しています:

void func(out MyType A) {
    A = new MyType(); 

    // do some other stuff here

    // take some time ... and return
}

これを次のように非同期で呼び出すと、次のようになります。

MyType a; 
func(out a); 

関数にAが割り当てられると、すぐに変更されますか?または、関数が戻ったときにのみ、新しいオブジェクト参照が割り当てられますか?

現在実装されている動作を中継したい場合の仕様はありますか?

4

4 に答える 4

4

関数でAが割り当てられると割り当てられます。を使用するとout、渡したものへの参照が提供されます。つまり、メソッドで変更されるたびに変更されます。

C#言語仕様からの抜粋:

5.1.6出力パラメータ

out修飾子で宣言されたパラメーターは、出力パラメーターです。

出力パラメーターは、新しい保管場所を作成しません。代わりに、出力パラメーターは、関数メンバーまたはデリゲート呼び出しで引数として指定された変数と同じ保管場所を表します。したがって、出力パラメーターの値は常に基になる変数と同じです。

次の明確な割り当てルールが出力パラメータに適用されます。§5.1.5で説明されている参照パラメータのさまざまなルールに注意してください。

・変数は、関数メンバーまたはデリゲート呼び出しで出力パラメーターとして渡す前に、明確に割り当てる必要はありません。

・関数メンバーまたはデリゲートの呼び出しが正常に完了した後、出力パラメーターとして渡された各変数は、その実行パスに割り当てられていると見なされます。

・関数メンバーまたは無名関数内では、出力パラメーターは最初は割り当てられていないと見なされます。

・関数メンバーまたは無名関数が正常に戻る前に、関数メンバーまたは無名関数のすべての出力パラメーターを確実に割り当てる必要があります(§5.3)。

構造体タイプのインスタンスコンストラクター内では、thisキーワードは構造体タイプの出力パラメーターとして正確に動作します(§7.6.7)。

于 2011-10-05T09:55:26.397 に答える
3

スレッドがA=new MyType();を実行するとすぐに変更されます。

于 2011-10-05T09:54:03.840 に答える
1

outもここで説明されていますが、短くて簡単なテストで、十分に速く知る必要があることがわかります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{
class Program
{
    static void TestOut(out int i)
    {
        i = 5;
        Thread.Sleep(5000);
    }

    static void Main(string[] args)
    {
        int i = 1;
        Console.WriteLine("i = " + i);
        Thread testOut = new Thread(new ThreadStart(() => TestOut(out i)));
        testOut.Start();
        Thread.Sleep(1000);
        Console.WriteLine("i = " + i);
        testOut.Join();
        Console.WriteLine("i = " + i);

        Console.WriteLine("Press ENTER to exit");
        Console.ReadLine();
    }
}
}

出力:

i = 1

i = 5

i = 5

編集:あなたのコメントに基づいて、以下を追加させてください:outキーワードの正式な言語仕様のドキュメントは、他の回答の1つで述べられているように、5.1.6章に限定されず、10.6.1.3章(p 307)でもカバーされています。次のとおりです。

10.6.1.3出力パラメーターout修飾子で宣言されたパラメーターは出力パラメーターです。参照パラメーターと同様に、出力パラメーターは新しい保管場所を作成しません。代わりに、出力パラメーターは、メソッド呼び出しで引数として指定された変数と同じ保管場所を表します。

仮パラメーターが出力パラメーターである場合、メソッド呼び出しの対応する引数は、キーワードoutと、それに続く仮パラメーターと同じタイプの変数参照(§5.3.3)で構成されている必要があります。変数を出力パラメーターとして渡す前に、変数を明確に割り当てる必要はありませんが、変数が出力パラメーターとして渡された呼び出しの後、変数は確実に割り当てられたと見なされます。

メソッド内では、ローカル変数と同様に、出力パラメーターは最初は未割り当てと見なされ、その値を使用する前に確実に割り当てる必要があります。

メソッドが戻る前に、メソッドのすべての出力パラメーターを確実に割り当てる必要があります。

部分メソッド(§10.2.7)またはイテレーター(§10.14)として宣言されたメソッドは、出力パラメーターを持つことができません。

出力パラメーターは通常、複数の戻り値を生成するメソッドで使用されます。例えば:

using System;

class Test
{
    static void SplitPath(string path, out string dir, out string name) {
        int i = path.Length;
        while (i > 0) {
            char ch = path[i – 1];
            if (ch == '\\' || ch == '/' || ch == ':') break;
            i--;
        }
        dir = path.Substring(0, i);
        name = path.Substring(i);
    }

    static void Main() {
        string dir, name;
        SplitPath("c:\\Windows\\System\\hello.txt", out dir, out name);
        Console.WriteLine(dir);
        Console.WriteLine(name);
    }
}

この例では、次の出力が生成されます。

c:\ Windows \ System \ hello.txt

dir変数とname変数は、SplitPathに渡される前に割り当てを解除でき、呼び出し後に確実に割り当てられたと見なされることに注意してください。

あなたが興味を持っているのはこのセクションです

参照パラメーターと同様に、出力パラメーターは新しい保管場所を作成しません。代わりに、出力パラメーターは、メソッド呼び出しで引数として指定された変数と同じ保管場所を表します。

これは、パラメータに独自の保存場所がないことを明確に示しているため、関数でその値を変更すると、代わりに元の値が変更されます。

于 2011-10-05T10:00:23.823 に答える
0

これは、参照とスレッドの安全性に関する質問ですが、スレッドセーフではないという結論に達しました。outはrefとほぼ同じです(outはnull参照もサポートします)。スレッドセーフではないという事実は、値がすぐに変更されることを意味します。

于 2011-10-05T10:06:44.970 に答える