「ref」キーワードと「out」キーワードの違いは何ですか?


891

私は、関数によって変更できるようにオブジェクトを渡す必要がある関数を作成しています。違いは何ですか:

public void myFunction(ref MyClass someClass)

そして

public void myFunction(out MyClass someClass)

どちらを使用する必要がありますか?その理由は?


69
あなた:変更できるようにオブジェクトを渡す必要があります。これはMyClassclassタイプ、つまり参照タイプのように見えます。その場合、渡すキーワードmyFunctionref/ outキーワードを付けなくても変更できます。myFunction同じオブジェクトを指す新しい参照を受け取り、同じオブジェクトを必要なだけ変更できます。違いrefキーワードになるだろうが、そのなりmyFunction受信同じ同じオブジェクトへの参照。これはmyFunction別のオブジェクトを指すように参照を変更する場合にのみ重要です。
Jeppe Stig Nielsen

3
@AnthonyKolesovが非常に完璧なとき、私はここで混乱する答えの量に困惑しています。
o0 '。

メソッドが複数の値を返すようにする場合は、outメソッドを宣言すると便利です。1つの引数をnullに割り当てることができます。これにより、メソッドはオプションで値を返すことができます。
Yevgraf Andreyevich Zhivago 2014

ここでは例それをより理解しやすい:)説明dotnet-tricks.com/Tutorial/csharp/...
Prageeth godage

2
@JeppeStigNielsenのコメントは、技術的には、OPの実際の質問に対する(唯一の)正解です。ようにメソッドにオブジェクトを渡す方法は、オブジェクトを変更することができ、単に値による方法にオブジェクト(参照)を通過します。メソッドに独自の変数(同じオブジェクトを参照する)が含まれている場合でも、object引数を使用してメソッド内のオブジェクトを変更すると、元のオブジェクトが変更されます。
デビッドRトリブル

回答:


1160

ref関数に入る前にオブジェクトが初期化されることをoutコンパイラーに伝え、オブジェクトが関数内で初期化されることをコンパイラーに伝えます。

ですから、ref双方向ですが、outアウトのみです。


270
outに固有のもう1つの優れた点は、関数がoutパラメータに割り当てる必要があることです。未割り当てのままにすることはできません。
Daniel Earwicker 2008

7
「ref」は値タイプにのみ適用されますか?参照型は常に参照渡しなので、
障害のある

3
はい。構造体を含む値タイプ
Rune Grimstad

17
@faulty:いいえ、refは値型にのみ適用されるわけではありません。ref / outは、C / C ++のポインターのようなもので、直接オブジェクトではなく、オブジェクトのメモリ位置(C#で間接的に)を処理します。
2010年

52
@faulty:直感に反して、参照指定型は、参照指定子を使用しない限り、常にC#の値で渡されます。myval = somenewvalを設定した場合、効果はその関数スコープ内にのみ存在します。refキーワードを使用すると、myvalを変更してsomenewvalを指すようにすることができます。
JasonTrue 2010

535

ref修飾子手段という。

  1. 値はすでに設定されており、
  2. メソッドはそれを読み取り、変更できます。

out修飾子手段という。

  1. Valueは設定されておらず、設定されるまでメソッドによって読み取ることができません。
  2. メソッド戻る前に設定する必要があります。

30
この回答は、refキーワードではなくoutキーワードを使用するときにコンパイラーが課す制限を最も明確かつ簡潔に説明しています。
Wily博士の実習生2010

5
MSDNから:refパラメーターは使用前に初期化する必要がありますが、outパラメーターは渡される前に明示的に初期化する必要はなく、以前の値は無視されます。
Shiva Kumar

1
ではout、メソッドが呼び出される前に初期化されていた場合、そのメソッドによって設定される前に、メソッド内でそれを読み取ることはできますか?つまり、呼び出されたメソッドは、呼び出したメソッドが引数としてそれに渡したものを読み取ることができますか?
Panzercrisis 2016年

3
Panzercrisis、「out」の場合、呼び出されたメソッドは、それがすでに設定されている場合は読み取ることができます。しかし、再度設定する必要があります。
robert jebakumar2

146

ドムがTPSレポートに関するメモについてピーターのキュービクルに現れたとしましょう。

ドムが参照論であるならば、彼はメモの印刷されたコピーを持っているでしょう。

ドムが反対論だった場合、彼はピーターに彼と一緒に持って行くためにメモの新しいコピーを印刷させるでしょう。


54
参照ドムは、ピーターがそれを修正できるように鉛筆でレポートを書いたでしょう
Deebster

6
@ディーブスターご存知、その比喩はあなたに何もしなかった、なぜあなたはそれを拷問しなければならないのですか?;)
マイケルブラックバーン

21
面白くて教育的で、stackoverflowはこのような投稿がさらに必要です
Frank Visaggio

2
誰かがこの答えをおかしいと思っただけの場合に備えて、映画「Office Space」をご覧ください。
displayName

ドムとピーターズの上司はドムの後ろに(議論として)立って、ピーターがドムドにプリントアウトを渡すまで、両方に新たにプリントアウトするように強いました
Patrick Artner

57

説明で手を試してみましょう:

値の型がどのように正しく機能するかを理解していると思いますか?値のタイプは(int、long、structなど)です。refコマンドなしでそれらを関数に送信すると、データがコピーされます。関数でそのデータに対して行うことは、コピーにのみ影響し、元には影響しません。refコマンドはACTUALデータを送信し、変更は関数外のデータに影響します。

わかりにくい部分に言及しましょう。参照型:

参照型を作成しましょう:

List<string> someobject = new List<string>()

someobjectを新規作成すると、2つの部分が作成されます。

  1. someobjectのデータを保持するメモリのブロック。
  2. そのデータブロックへの参照(ポインタ)。

ここで、refなしでsomeobjectをメソッドに送信すると、データではなく参照ポインターがコピーされます。だからあなたは今これを持っています:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

同じオブジェクトを指す2つの参照。reference2を使用してsomeobjectのプロパティを変更すると、reference1が指す同じデータに影響します。

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

reference2をnullにするか、それを新しいデータにポイントしても、reference1には影響せず、データreference1がポイントしません。

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

メソッドへの参照によってsomeobjectを送信するとどうなりますか?someobjectへの実際の参照メソッドに送信されます。これで、データへの参照は1つだけになりました。

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

しかし、これはどういう意味ですか?これは、2つの主なことを除いて、refではなくsomeobjectを送信するのとまったく同じように動作します。

1)メソッド内の参照をnullにすると、メソッド外の参照がnullになります。

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2)これで、完全に異なるデータの場所を参照するように指定でき、関数の外部の参照は新しいデータの場所を参照するようになります。

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true

結局のところ(refの場合)、データへの参照は1つだけですが、それに対する2つのエイリアスがあります。正しい?
Sadiq 2015

3
明確な説明に賛成。しかし、これはrefoutパラメータの違いを説明しないので、質問には答えません。
Joyce Babu 2016

1
すごい。outキーワードと同じように説明できますか?
Asif Mushtaq 2016

28

refはインアウトです。

out要件を満たすのに十分な場所で優先的に使用する必要があります。


方向性がある場合は受け入れられた回答refとして渡されるので、値型を無視しても役に立たないため、正確ではありません。
ケニー

@kenny:もう少し明確にしてください-つまり、答えの精神を維持するためにどの単語を変更しますか?私の答えは初心者からの狂った推測ではありませんが、あなたのコメントの速攻(簡潔さ、タイプミス)はそれがそうであると想定しているようです。目的は、最小の単語数で違いについて考える方法を提供することです。
Ruben Bartelink

(ところで、値の型、参照の型、参照による受け渡し、値による受け渡し、COMとC ++は、説明の中でそれらの概念を参照するのに役立つと
わかって

1
オブジェクト参照は値で渡されます( "ref"または "out"キーワードを使用する場合を除く)。オブジェクトはID番号と考えてください。クラス変数が「オブジェクト#1943」を保持し、その変数を値でルーチンに渡す場合、そのルーチンはオブジェクト#1943を変更できますが、変数が「オブジェクト#1943」以外を指すようにすることはできません。変数が参照によって渡された場合、ルーチンは変数ポイントに「オブジェクト#5441」を保持させることができます。
スーパーキャット2012年

1
@supercat:refとval(およびこのフォローアップ分析)の説明が好きです。私はケニーは実際に彼に説明されたこれのいずれも必要としないと思います、彼のコメントがそうであったように、(比較的)混乱しています。みんなを混乱させているだけなのに、これらのゴッドダムのコメントをすべて削除できればいいのにと思います。このすべてのナンセンスの根本的な原因は、ケニーが私の答えを誤って読み、追加/削除/置換すべき1つの単語をまだ指摘していないことです。私たち3人は、私たちがまだ知らなかったディスカッションから何も学んでおらず、他の回答には、途方もない数の賛成票があります。
Ruben Bartelink 2012年

18

アウト:

C#では、メソッドは1つの値のみを返すことができます。複数の値を返したい場合は、outキーワードを使用できます。out修飾子は、参照渡しとして返されます。最も簡単な答えは、メソッドから値を取得するためにキーワード「out」が使用されることです。

  1. 呼び出し元の関数で値を初期化する必要はありません。
  2. 呼び出された関数に値を割り当てる必要があります。そうしないと、コンパイラーがエラーを報告します。

ref:

C#では、int、float、doubleなどの値の型を引数としてメソッドパラメータに渡すと、値によって渡されます。したがって、パラメーター値を変更しても、メソッド呼び出しの引数には影響しません。ただし、パラメーターを「ref」キーワードでマークすると、実際の変数に反映されます。

  1. 関数を呼び出す前に変数を初期化する必要があります。
  2. メソッドのrefパラメーターに値を割り当てることは必須ではありません。値を変更しない場合、「参照」としてマークする必要は何ですか?

「C#では、メソッドは1つの値しか返すことができません。複数の値を返したい場合は、outキーワードを使用できます。」戻り値に「ref」を使用することもできます。メソッドから複数の値を返したい場合は、refとoutの両方を使用できますか?
2018年

1
C#7では、ValueTuplesを使用して複数の値を返すことができます。
Iman Bahrampour 2018

13

Dog、Catの例を拡張します。refを使用した2番目のメソッドは、呼び出し元が参照するオブジェクトを変更します。したがって、「猫」!!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

8

参照型(クラス)を渡すrefので、デフォルトでは実際のオブジェクトへの参照のみが渡されるため、参照の背後にあるオブジェクトを常に変更するため、使用する必要はありません。

例:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

refメソッド内のオブジェクトを変更する場合は、クラスを渡す限り、使用する必要はありません。


5
これは、新しいオブジェクトが作成および返されない場合にのみ機能します。新しいオブジェクトが作成されると、古いオブジェクトへの参照は失われます。
etsuba 08

8
これは間違っています-次を試してください:実行someObject = nullBar終了するために追加します。Barインスタンスへのの参照のみがnullになったため、コードは正常に実行されます。次に、に変更BarBar(ref MyClass someObject)て再度実行します。インスタンスへのの参照もnullになっているNullReferenceExceptionため、を取得しFooます。
キース

8

refそしてout次のような違いを除いて同様に振る舞います。

  • ref変数は使用前に初期化する必要があります。out変数は割り当てなしで使用できます
  • outパラメータは、それを使用する関数によって、割り当てられていない値として扱われる必要があります。したがって、out呼び出しコードで初期化パラメーターを使用できますが、関数が実行されると値は失われます。


6

「パン屋」

これは、最初の文字列が文字列参照を「Baker」を指すように変更するためです。refキーワード(=>文字列への参照への参照)を介して渡したため、参照の変更が可能です。2番目の呼び出しは、文字列への参照のコピーを取得します。

文字列は、最初はある種の特別なものに見えます。しかし、文字列は単なる参照クラスであり、定義すると

string s = "Able";

次に、sは「Able」というテキストを含む文字列クラスへの参照です。同じ変数への別の割り当て

s = "Baker";

元の文字列は変更せず、新しいインスタンスを作成して、そのインスタンスを指すようにします。

次の小さなコード例でそれを試すことができます:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

何を期待していますか?s2が元のインスタンスを指している間、sの参照を別のインスタンスに設定するだけなので、取得できるのは依然として「Able」です。

編集:文字列も不変です。つまり、既存の文字列インスタンスを変更するメソッドまたはプロパティがないだけです(ドキュメント内で文字列を検索することはできますが、検索はできません:-))。すべての文字列操作メソッドは新しい文字列インスタンスを返します!(そのため、StringBuilderクラスを使用するとパフォーマンスが向上することがよくあります)


1
丁度。したがって、「参照型(クラス)を渡しているので、use refを使用する必要はない」と言っても厳密には当てはまりません。
ポールミッチェル

理論的には、彼が「変更できるように」と書いたのは、文字列では不可能であるため、そう言うのは正しいことです。しかし、不変オブジェクトのため、「ref」と「out」は参照型にも非常に役立ちます。(.Netには不変クラスがたくさん含まれています!)
mmmmmmmm

はい、あなたが正しい。ほとんどのオブジェクトは可変であるため、文字列などの不変オブジェクトについては考えていません。
アルビック

1
確かに、これはLQPで確認する不可解な答えです。これはフォーラムであるかのように、別のコメントへの長くて完全な応答であるように見えることを除いて、問題はありません。私はそれがまだずっと前に本当に整理されていなかったと思います。
Nathan Tuggy、

6

refは、refパラメーターの値が既に設定されていることを意味し、メソッドはその値を読み取って変更できます。refキーワードを使用することは、呼び出し元がパラメーターの値を初期化する責任があると言うことと同じです。


outは、オブジェクトの初期化が関数の責任であることをコンパイラに通知します。関数はoutパラメータに割り当てる必要があります。未割り当てのままにすることはできません。


5

Out: returnステートメントは、関数から1つの値のみを返すために使用できます。ただし、出力パラメーターを使用すると、関数から2つの値を返すことができます。出力パラメーターは、データをメソッド内ではなくメソッド外に転送することを除いて、参照パラメーターに似ています。

次の例はこれを示しています。

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

ref: 参照パラメーターは、変数のメモリ位置への参照です。値パラメーターとは異なり、パラメーターを参照で渡す場合、これらのパラメーターの新しい保管場所は作成されません。参照パラメーターは、メソッドに提供される実際のパラメーターと同じメモリ位置を表します。

C#では、refキーワードを使用して参照パラメーターを宣言します。次の例は、これを示しています。

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}

4

refとoutは、C ++の場合のように、参照渡しとポインタ渡しと同じように機能します。

refの場合、引数を宣言して初期化する必要があります。

outの場合、引数は宣言する必要がありますが、初期化される場合とされない場合があります

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);

1
変数はインラインで宣言できますout double Half_nbr
セバスチャンホフマン2018

4

作成時間:

(1)呼び出しメソッドを作成する Main()

(2)リストオブジェクト(参照型オブジェクト)を作成し、変数に格納しますmyList

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

実行時:

(3)ランタイムは、アドレスを格納するのに十分な幅で、#00にスタック上のメモリを割り当てます(myList変数名は実際にはメモリロケーションの単なるエイリアスであるため、#00 = )

(4)ランタイムはヒープ上のリストオブジェクトをメモリ位置#FFに作成します(これらのアドレスはすべて例のためです)

(5)次に、ランタイムはオブジェクトの開始アドレス#FFを#00に格納します(つまり、Listオブジェクトの参照をポインターに格納しますmyList

オーサリング時間に戻る:

(6)次にmyParamList、呼び出されたメソッドに引数としてListオブジェクトを渡し、modifyMyListそれに新しいListオブジェクトを割り当てます

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

実行時:

(7)ランタイムは、呼び出されたメソッドの呼び出しルーチンを開始し、その一部として、パラメーターのタイプをチェックします。

(8)参照型を見つけると、パラメーター変数のエイリアスを作成するために、#04のスタックにメモリを割り当てますmyParamList

(9)次に、値#FFも格納します。

(10)ランタイムは、メモリロケーション#004のヒープにリストオブジェクトを作成し、#04の#FFをこの値で置き換えます(または、元のリストオブジェクトを逆参照し、このメソッドで新しいリストオブジェクトをポイントします)

#00のアドレスは変更されず、#FFへの参照を保持します(または元のmyListポインターは影響を受けません)。


REFのキーワードは、メソッドのパラメータのためのヒープ割り当てがないことを意味する(8)及び(9)のランタイムコードの生成をスキップするコンパイラディレクティブです。オリジナルの#00ポインターを使用して、#FFにあるオブジェクトを操作します。元のポインターが初期化されていない場合、変数は初期化されていないため、ランタイムはそれが先に進めないと警告します。

アウトキーワードはかなり(9)及び(10)にわずかな変更でREFと同じであるコンパイラ・ディレクティブです。コンパイラーは、引数が初期化されていないことを期待し、(8)、(4)、および(5)を続行して、ヒープ上にオブジェクトを作成し、その開始アドレスを引数変数に格納します。初期化されていないエラーはスローされず、保存されている以前の参照は失われます。


3

他の誰かの変数をクラスの別のインスタンスに再割り当てたり、複数の値を返したりすることができるほか、他の誰かがそれらから必要なものを提供したり、提供した変数で何をしたいのかを知らせたりすることができますrefout

  • あなたは必要としない refか、outあなたがやろうとしているすべての修正のものであれば内部のMyClass引数で渡されるインスタンスsomeClass

    • 呼び出しメソッドは、someClass.Message = "Hello World"を使用するかrefout何も使用しないかのような変更を確認します
    • someClass = new MyClass()内部で書き込むと、メソッドのスコープ内のみで表示myFunction(someClass)されるオブジェクトがスワップアウトされます。呼び出し元のメソッドは、それが作成してメソッドに渡した元のインスタンスを認識しています。someClassmyFunctionMyClass
  • あなたは必要 refout、あなたがスワップを予定している場合someClass、まったく新しいオブジェクトのためのアウトをして、あなたの変更を確認するために呼び出すメソッドをしたいです

    • someClass = new MyClass()内部に書き込むmyFunction(out someClass)と、呼び出されたメソッドから見えるオブジェクトが変更されますmyFunction

他のプログラマーが存在する

そして、彼らはあなたが彼らのデータで何をしようとしているのかを知りたいのです。何百万人もの開発者が使用するライブラリを書いているとしましょう。あなたは彼らがあなたのメソッドを呼び出すときに彼らが変数をどうしようとしているのかを彼らに知らせたい

  • を使用refすると、「メソッドを呼び出すときに、ある値に割り当てられた変数を渡す。メソッドの実行中に、変数を別のものに変更する可能性があることに注意してください。変数が古いオブジェクトを指しているとは期待しないでください。終わったら」

  • を使用outすると、「プレースホルダー変数をメソッドに渡します。値があるかどうかは関係ありません。コンパイラーはそれを新しい値に割り当てることを強制します。私は、あなたは私のメソッドを呼び出し、変数の前に、ます、私は終わりだ、時間によって異なること

ちなみに、C#7.2にはin修飾子もあります

そして、それによって、メソッドが、渡されたインスタンスを別のインスタンスにスワップアウトするのを防ぎます。何百万人もの開発者に「元の変数参照を渡してください。細心の注意を払って作成したデータを他のものに交換しないことを約束します」と言っているようなものだと考えてください。inにはいくつかの特徴がありin int、コンパイラとの互換性を確保するために暗黙の変換が必要になる場合など、場合によっては一時的にintを作成し、shortをそれに広げ、参照によって渡して仕上げます。あなたはそれを台無しにするつもりはないと宣言したので、これを行うことができます。


Microsoftはこれを.TryParse数値型のメソッドで行いました。

int i = 98234957;
bool success = int.TryParse("123", out i);

out彼らがここで積極的に宣言しているときにパラメーターにフラグを設定することにより、「私たちは間違いなく細心の注意を払って作成された98234957の値を他のものに変更します」

もちろん、値の型の解析などでは、値の型を別のものにスワップアウトすることが許可されていなかった場合、うまく機能しないため、それらは少し必要になります。しかし、一部の架空のメソッドが作成しているライブラリ:

public void PoorlyNamedMethod(out SomeClass x)

あなたはそれoutがであることがわかります。したがって、数を計算するのに何時間も費やした場合、完璧なSomeClassを作成していることがわかります。

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

まあ、それは時間の無駄でした、その完璧なクラスを作るためにそれらすべての時間を費やしました。間違いなく破棄され、PoorlyNamedMethodに置き換えられます


3

簡潔な答えを探している人のために。

両方refoutキーワードを渡しバイために使用されていますreference


refキーワードの変数は、値を持っているか、オブジェクトを参照するか、オブジェクトを渡すnull 前に参照する必要があります。


とは異なりrefoutkeywordの変数は、値を持っているか、オブジェクトを参照するかnull その受け渡し後に参照する必要があります。また、渡す前に値を持っているか、オブジェクトを参照する必要はありません。


2

多くの優れた説明を説明するために、次のコンソールアプリを開発しました。

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
  • AppendWorldStringListnamed のコピーLiStriが渡されました。メソッドの開始時に、このコピーは元のリストを参照するため、このリストを変更するために使用できます。後でメソッド内のLiStri別のList<string>オブジェクトを参照しますが、元のリストには影響しません。

  • HalloWeltLiStriRefはすでに初期化されているのエイリアスです ListStringRef。渡されたList<string>オブジェクトは新しいオブジェクトを初期化するために使用されるため、ref必要でした。

  • CiaoMondoLiStriOutはのエイリアスでListStringOutあり、初期化する必要があります。

したがって、メソッドが渡された変数によって参照されるオブジェクトを変更するだけの場合、コンパイラーは使用outを許可せず、コンパイラーではなくrefコードのリーダーを混乱させるため、使用しないでください。メソッドが渡された引数が別のオブジェクトを参照するようにする場合はref、既に初期化されているオブジェクトとout、渡された引数に対して新しいオブジェクトを初期化する必要があるメソッドに使用します。それに加えて、refout同じように動作。


1

それらはほとんど同じです-唯一の違いは、outパラメーターとして渡す変数を初期化する必要がないことと、refパラメーターを使用するメソッドがそれを何かに設定する必要があることです。

int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

参照パラメーターは、変更される可能性のあるデータ用であり、出力パラメーターは、何かの戻り値を既に使用している関数(int.TryParseなど)の追加出力であるデータ用です。


1

以下に、Refoutの両方を使用した例を示します。今、あなたはすべてrefとoutについてクリアされます。

下記の例でコメントすると// myRefObj = new myClass {Name = "ref outside called !!"}; 「未割り当てのローカル変数 'myRefObj'の使用」 というエラーが表示さますが、outにはそのようなエラーはありません。

Refを使用する場所:inパラメーターを使用してプロシージャを呼び出す場合、同じパラメーターがそのprocの出力を格納するために使用されます。

Outを使用する場所: inパラメーターなしでプロシージャを呼び出している場合、同じパラメーターを使用して、そのprocから値を返します。出力にも注意してください

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 

1
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

このコードをチェックして、「ref」を使用したときの完全な違いを説明できます。これは、そのint / stringがすでに初期化されていることを意味します

しかし、 "out"を使用すると、uがそのint / stringを初期化するかどうかにかかわらず、両方の条件で機能しますが、その関数でそのint / stringを初期化する必要があります。


1

Ref:refキーワードは、引数を参照として渡すために使用されます。つまり、そのパラメーターの値がメソッドで変更されると、呼び出し元のメソッドに反映されます。refキーワードを使用して渡される引数は、呼び出されるメソッドに渡される前に、呼び出すメソッドで初期化する必要があります。

Out:outキーワードはrefキーワードと同様に引数を渡すためにも使用されますが、値を割り当てることなく引数を渡すことができます。outキーワードを使用して渡される引数は、呼び出し元のメソッドに戻る前に、呼び出されたメソッドで初期化する必要があります。

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

メソッドのオーバーロードでの参照と出力

メソッドオーバーロードでは、refとoutの両方を同時に使用することはできません。ただし、refとoutは実行時に異なる方法で処理されますが、コンパイル時には同じように処理されます(CLRは、refとoutのILを作成している間、2つを区別しません)。


0

パラメータを受け取る方法の観点から、との違いrefとは、outC#は、方法は、すべてに書かなければならないことを必要とすることであるout戻す前に、パラメータ、およびとして渡す以外に、このようなパラメータで何もしてはならないoutことへのパラメータまたは書き込み、outパラメータとして別のメソッドに渡されるか、直接書き込まれるまで。他のいくつかの言語はそのような要件を課さないことに注意してください。outパラメータを使用してC#で宣言された仮想メソッドまたはインターフェイスメソッドは、そのようなパラメータに特別な制限を課さない別の言語でオーバーライドできます。

呼び出し元の観点から、C#は多くの状況で、outパラメーターを使用してメソッドを呼び出すと、渡された変数が最初に読み取られずに書き込まれると想定します。他の言語で記述されたメソッドを呼び出す場合、この仮定は正しくない場合があります。例えば:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

C#以外の言語で記述されmyDictionaryIDictionary<TKey,TValue>実装を識別する場合MyStruct s = new MyStruct(myDictionary);、割り当てのように見えても、s変更されないままになる可能性があります。

C#のコンストラクターとは異なり、VB.NETで作成されたコンストラクターは、呼び出されたメソッドがoutパラメーターを変更するかどうかを想定せず、すべてのフィールドを無条件にクリアすることに注意してください。上記で言及した奇妙な動作は、完全にVBまたはC#で記述されたコードでは発生しませんが、C#で記述されたコードがVB.NETで記述されたメソッドを呼び出すと発生する可能性があります。


0

パラメータを参照として渡す場合は、パラメータを関数に渡す前に初期化する必要があります。そうしないと、コンパイラ自体にエラーが表示されます。ただし、outパラメータの場合は、オブジェクトパラメータを初期化してからオブジェクトに渡す必要はありません。メソッド。呼び出し元のメソッド自体でオブジェクトを初期化できます。


-3

関数内で渡される参照パラメーターが直接処理されることに注意してください。

例えば、

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

これはCatではなくDogと書きます。したがって、someObjectを直接操作する必要があります。


6
ここでのすべてのことはほとんど真実ですが、値による参照と出力の違いを実際に説明しているわけではありません。せいぜい半分は、参照型と値/不変型の違いを説明しています。
Conrad Frix、

そのコードでcatを作成する場合は、次のようにそのオブジェクトを 'ref'キーとともに渡します。public static void Bar(ref MyClass someObject)、Bar(ref myObject);
Daniel Botero Correa

-4

私はこれがあまり得意ではないかもしれませんが、確かに文字列(技術的には参照型であり、ヒープ上に存在します)は参照ではなく値で渡されますか?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

このため、変更を行う関数のスコープ外に変更を存在させたい場合はrefが必要ですが、それ以外の場合は参照を渡しません。

私が知っている限り、構造体/値型と文字列自体の参照のみが必要です。文字列は、それを偽る参照型であるため、値型ではありません。

ここで私は完全に間違っているかもしれません、私は新しいです。


5
エドウィン、スタックオーバーフローへようこそ。文字列は、私の知る限り、他のオブジェクトと同じように参照渡しされます。文字列は不変のオブジェクトであるため、混乱する可能性があります。そのため、文字列が参照によって渡されることはそれほど明白ではありません。文字列Capitalize()に、文字列の内容を大文字に変更するメソッドが呼び出されたとします。その後、行a = "testing";a.Capitalize();に置き換えた場合、出力は「Hello」ではなく「HELLO」になります。不変タイプの利点の1つは、参照を渡すことができ、他のコードが値を変更する心配がないことです。
Don Kirkby

2
型が公開できるセマンティクスには、3つの基本的なタイプがあります。可変参照セマンティクス、可変値セマンティクス、および不変セマンティクスです。フィールドまたはプロパティmを持つタイプTの変数xおよびyを考え、xがyにコピーされると仮定します。Tに参照セマンティクスがある場合、xmへの変更はymによって監視されます。Tに値セマンティクスがある場合、ymに影響を与えずにxmを変更できます。Tが不変のセマンティクスを持っている場合、xmもymも変更されません。不変のセマンティクスは、参照オブジェクトまたは値オブジェクトのいずれかによってシミュレートできます。文字列は不変の参照オブジェクトです。
スーパーキャット2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.