767

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

public void myFunction(ref MyClass someClass)

そして

public void myFunction(out MyClass someClass)

どちらを使用すればよいですか、またその理由は何ですか。


  • 君は:変更できるようにオブジェクトを渡す必要がありますそれはように見えますMyClassになりますclasstype、つまり参照型。その場合、渡したオブジェクトは次のように変更できます。myFunctionいいえでもref/outキーワード。myFunctionを受け取ります新しいを指す参照同じそして、同じオブジェクトを必要なだけ変更できます。違いはrefキーワードは作るだろう、ということmyFunctionを受け取りました同じ同じオブジェクトへの参照それが重要なのはmyFunctionへの参照を変更することでした別のオブジェクト - Jeppe Stig Nielsen
  • @ AnthonyKolesovが非常に完璧であるとき、私はここで混乱している答えの量に困惑しています。 - o0'.
  • メソッドが複数の値を返すようにする場合は、outメソッドを宣言すると便利です。 1つの引数をnullに割り当てることができます。これにより、メソッドはオプションで値を返すことができます。 - Yevgraf Andreyevich Zhivago
  • ここでは例を挙げて説明します。dotnet-tricks.com/Tutorial/csharp/… - Prageeth godage
  • @ JeppeStigNielsenのコメントは、技術的には、OPの実際の質問に対する(唯一の)正しい答えです。オブジェクトをメソッドに渡して、メソッドはオブジェクトを変更できます単純に、(オブジェクトへの参照)オブジェクトを値でメソッドに渡します。 object引数を介してメソッド内のオブジェクトを変更する元のオブジェクトを変更しますメソッドに独自の独立した変数(同じオブジェクトを参照する)が含まれていても。 - David R Tribble

24 답변


1022

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

そうながらref双方向ですoutアウトのみです。


  • outに特有のもう一つのクールなことは、関数がoutパラメータに代入しなければならないということです。未割り当てのままにすることは許可されていません。 - Daniel Earwicker
  • & ref'値型にのみ適用可能ですか?参照型は常にrefによって渡されるので。 - faulty
  • はい。構造体を含む値型 - Rune Grimstad
  • @faulty:いいえ、refは値型にのみ適用されるわけではありません。 ref / outはC / C ++のポインタのようなもので、直接オブジェクトではなくオブジェクトのメモリ位置(C#では間接的)を扱います。 - thr
  • @faulty:直感に反して、ref指定子を使用しない限り、参照型は常にC#の値によって渡されます。 myval = somenewvalを設定した場合、効果はその関数スコープ内だけにあります。 refキーワードを使用すると、myvalをsomenewvalを指すように変更できます。 - JasonTrue

447

ref修飾子は、

  1. 値はすでに設定されています
  2. メソッドはそれを読んで修正することができます。

out修飾子は、

  1. 値が設定されておらず、メソッドで読み取ることができませんまでそれが設定されています。
  2. メソッドしなければならない戻る前に設定してください。


  • この回答は、refキーワードではなくoutキーワードを使用するときにコンパイラが課す制限を最も明確かつ簡潔に説明しています。 - Dr. Wily's Apprentice
  • MSDNから:refパラメータは使用前に初期化する必要がありますが、outパラメータは渡される前に明示的に初期化する必要はなく、以前の値は無視されます。 - Shiva Kumar
  • ありoutメソッドが呼び出される前に初期化されている場合、そのメソッドによって設定される前に、メソッド内で完全に読み取ることができますか。つまり、呼び出されたメソッドは、呼び出し元のメソッドが引数として渡したものを読み取ることができますか? - Panzercrisis
  • Panzercrisis、「out」の場合、呼び出されたメソッドはすでに設定されている場合は読み取ることができます。しかし、それは再び設定しなければなりません。 - robert jebakumar2

135

DomがTPSレポートに関するメモについてPeterのブースに登場したとしましょう。

もしDomがrefの議論であれば、彼はそのメモの印刷されたコピーを持っているでしょう。

もしDomが議論の余地がなかったら、彼は彼が彼と一緒に持っていくために彼にそのメモの新しいコピーを印刷させるでしょう。


  • ref Domは報告書を鉛筆で書いてペトロがそれを修正できるようにしたはずです。 - Deebster
  • あなたが知っている@Deebster、その隠喩はあなたに何もしなかった、なぜあなたはそれを拷問しなければならないのですか? ;) - Michael Blackburn
  • 面白くても教育的で、stackoverflowはこのようなより多くの投稿を必要とします - Frank Visaggio
  • 万が一誰かがこの答えを面白くないと思った場合は、映画「Office Space」をご覧ください。 - displayName
  • そして、DomとPetersの上司がDomのすぐ後に立って、PeterがDomdに印刷を手渡すまで、両方を新たに印刷しようとした。 - Patrick Artner

48

私は説明を試みるつもりです:

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

わかりにくい部分は、参照型です。

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

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

あなたが新しいとき誰か2つの部分が作成されます。

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

今あなたが送るとき誰か参照せずにメソッドに変換する参照データではなくポインタ。だから今あなたはこれを持っている:

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

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

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

reference2を無効にしたり、新しいデータを指し示したりしても、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

今あなたが送るとき何が起こるか誰かメソッドへの参照によって? の実際の参照誰かメソッドに送信されます。これで、データへの参照は1つだけになりました。

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

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

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

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

2)参照を完全に異なるデータ位置に向けることができ、関数外の参照は新しいデータ位置を指すようになります。

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


  • つまり、(参照の場合)データへの参照は1つだけで、それに対するエイリアスは2つだけです。右? - Sadiq
  • 明確な説明を支持した。しかし、私はこれが質問に答えていないと思います。refそしてoutパラメーター。 - Joyce Babu
  • すごい。と同じように説明できますかoutキーワード? - Asif Mushtaq
  • cppのようにref型を渡す方法を理解しようとしていましたが、refキーワードを使ってref型を渡すのは賢明ではありませんでした。 - Phoera

27

refが入っていますそしてでる

あなたが使うべきですoutそれがあなたの要求に十分であるところはどこでも優先して。


  • 受け入れられた答えが返答されていない場合には方向性があり無意味な値型を無視するならば参照されるので、まったくそうではありません。 - kenny
  • @kenny:あなたは少し喜ばしいことを明確にすることができます - すなわち、答えの精神を維持しながらあなたが理解する不正確さを取り除くためにどの単語を変えるでしょうか?私の答えは初心者からのクレイジーな推測ではありませんが、あなたのコメントの速さ(簡潔さ、タイプミス)はそうであると仮定しているようです。目的は、単語の数が最も少ない違いについて考える方法を提供することです。 - Ruben Bartelink
  • (ところで、値の種類、参照の種類、参照渡し、値渡し、COM、C ++に精通している方は、説明の中でこれらの概念を参照すると便利です。) - Ruben Bartelink
  • オブジェクト参照は値で渡されます(" ref"または" out"キーワードを使用する場合を除く)。オブジェクトをID番号と考えてください。クラス変数が" Object#1943"を保持している場合その変数を値でルーチンに渡すと、そのルーチンはObject#1943に変更を加えることができますが、その変数が "Object#1943"以外のものを指すことはできません。変数が参照によって渡された場合、ルーチンは変数ポイントに「Object#5441」を保持させることができます。 - supercat
  • @supercat:refとval(そしてこのフォローアップのアナロジー)の説明が好きです。私はケニーが実際に彼に説明されたこれらのいずれかを必要としないと思います、彼のコメントがそうであったように(比較的)混乱します。私たち全員がこれらのゴッダムコメントを削除することができればいいのにと思っています。このナンセンスの根本的な原因は、kennyが私の答えを誤解しており、まだ追加/削除/置換されるべきである単一の単語を指摘していないことのようです。私たち3人のうち3人は誰も私たちがこれまでに知らなかった議論から何かを学んだことがなく、他の答えには滑稽な数の支持があります。 - Ruben Bartelink

15

でる:

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

  1. 呼び出し元の関数で値を初期化する必要はありません。
  2. 呼び出された関数に値を代入しなければ、コンパイラはエラーを報告します。

参照:

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

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


  • " C#では、メソッドは1つの値しか返すことができません。複数の値を返す場合は、outキーワードを使用できます。 " ref"を使用することもできます。値を返します。メソッドから複数の値を返す場合は、refとoutの両方を使用できますか。 - Ned
  • c#7では、ValueTuplesを使って複数の値を返すことができます。 - Iman Bahrampour

12

犬、猫の例を拡張する。 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メソッド内のオブジェクトを変更したい場合


  • これは、新しいオブジェクトが作成されて返されない場合にのみ機能します。新しいオブジェクトが作成されると、古いオブジェクトへの参照は失われます。 - etsuba
  • これは間違っています - 以下を試してください。someObject = nullBar実行を終了します。あなたのコードは以下のようにうまく動くBarインスタンスへの参照は無効になりました。今すぐ変更BarBar(ref MyClass someObject)その後、もう一度実行してください。NullReferenceExceptionなぜならFooインスタンスへの参照も無効になりました。 - Keith

7

refそしてout以下の違いを除いて同様に動作します。

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


6

「パン屋」

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

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

string s = "Able";

それからsは "Able"というテキストを含む文字列クラスへの参照です。 同じ変数への別の代入

s = "Baker";

元の文字列を変更するのではなく、新しいインスタンスを作成してそのインスタンスをポイントさせるだけです。

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

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

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

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


  • その通りです。したがって、「参照型(クラス)を渡すので、refを使用する必要はない」と言うのは厳密には正しくありません。 - Paul Mitchell
  • 理論的には、「修正できるように」と書いているので、そう言うのは正しいことです。これは文字列では不可能です。しかし、不変オブジェクト「ref」のためです。と「out」参照型にも非常に便利です。 (.Netにはたくさんの不変クラスが含まれています!) - mmmmmmmm
  • はい、あなたは正しいです。ほとんどのオブジェクトはミュータブルなので、不変オブジェクトを文字列のように考えることはしませんでした。 - Albic
  • 確かに、これはLQPで見るべき不可解な答えです。これはフォーラムであるかのように、それが別のコメントに対する長く徹底的な回答であることを除いて(最初の質問ではAbleとBakerについて言及していないため)、それは問題ありません。まだ整理されていなかったと思います。 - Nathan Tuggy

6

例によって(私のように)学ぶ人のためにここに何があるかAnthony Kolesovが言っています

この点を説明するために、ref、outなどの最小限の例をいくつか作成しました。ベストプラクティスは扱っていません。違いを理解するための例にすぎません。

https://gist.github.com/2upmedia/6d98a57b68d849ee7091


4

でる:関数から1つの値だけを返すためにreturn文を使うことができます。ただし、出力パラメータを使用すると、関数から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();

      }
   }
}

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

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);


  • 変数をインラインで宣言することができます。out double Half_nbr。 - Sebastian Hofmann

4

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


でるobjectの初期化はの責任であることをコンパイラに伝えます 関数、関数はoutパラメータに割り当てる必要があります。 未割り当てのままにすることはできません。


3

オーサリング時間:

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

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

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

ランタイム中:

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

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

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

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

(6)次にListオブジェクトを引数として渡しますmyParamList呼び出されたメソッドへ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をこの値で置き換えます(または元のListオブジェクトを間接参照し、このメソッドで新しいListオブジェクトを参照します)。

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


参照keywordは、(8)および(9)のランタイムコードの生成をスキップするコンパイラ指令です。これは、メソッドパラメータのヒープ割り当てがないことを意味します。 #FFでオブジェクトを操作するために、元の#00ポインタを使用します。元のポインタが初期化されていない場合は、変数が初期化されていないため、ランタイムはそれを進めることができないと文句を言って停止します。

でるkeywordは(9)と(10)を少し変更しただけのrefとほぼ同じコンパイラ指令です。コンパイラーは引数が初期化されていないと想定し、ヒープ上にオブジェクトを作成し、その開始アドレスを引数変数に格納するために(8)、(4)、(5)を続けます。初期化されていないエラーはスローされず、以前に保存された参照はすべて失われます。


1

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

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

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


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"を使うとき、それはuがそのint / stringをすでに初期化しているという意味です

しかし  あなたが "out"を使うとき それはどちらの条件でもうまく動作します。 しかし、uはその関数内でそのint / stringを初期化しなければなりません


1

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

でる: 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は実行時には異なる方法で処理されますが、コンパイル時には同じように処理されます(refとoutに対してILを作成する間、CLRは両者を区別しません)。


0

以下に両方を使った例を示しました参考そしてでる。今、あなたはすべてrefとoutについてクリアされます。

下記の例で私がコメントするとき// myRefObj = new myClass {Name = "参照が呼び出されました!!"};行、と言ってエラーが発生します"割り当てられていないローカル変数 'myRefObj'の使用"しかし、そのようなエラーはありませんでる

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; }
} 


0

パラメータを受け取るメソッドの観点からは、refそしてoutC#はすべてのメソッドに書き込む必要があるということです。outパラメータを返す前に、そのようなパラメータに対して何もしてはいけません。outパラメータとして渡されるか、パラメータとして書き込まれるout別のメソッドへのパラメータ、または直接書き込まれます。他のいくつかの言語はそのような要求を課さないことに注意してください。 C#で宣言された仮想メソッドまたはインタフェースメソッドoutパラメータは、そのようなパラメータに特別な制限を課さない別の言語で上書きされる可能性があります。

呼び出し側から見れば、多くの状況でC#は、outparameterを指定すると、渡された変数は最初に読み取られることなく書き込まれます。他の言語で書かれたメソッドを呼び出すとき、この仮定は正しくないかもしれません。例えば:

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

もしmyDictionaryを識別するIDictionary<TKey,TValue>とはいえ、C#以外の言語で書かれた実装MyStruct s = new MyStruct(myDictionary);代入のように見えます、それは潜在的に残すことができますs未修正。

C#のものとは異なり、VB.NETで作成されたコンストラクタは、呼び出されたメソッドが何らかの変更を加えるかどうかについて仮定しないことに注意してください。outパラメータを設定し、すべてのフィールドを無条件に消去します。上で暗示された奇妙な振る舞いは完全にVBまたは完全にC#で書かれたコードでは起こりませんが、C#で書かれたコードがVB.NETで書かれたメソッドを呼び出すときに起こり得ます。


0

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


-1

私はと遊んでいたrefそして、この例は非常に興味深いことがわかりました。 私はの呼び出しを考えたRefEater(ref s1);2番目のコメント付きケースのようにビルドエラーを引き起こしますが、s1コンストラクタが呼び出される前に、fieldはデフォルト値に初期化されます(https://stackoverflow.com/a/1920659/5612780

public class Class1
{
    // this will have the default value
    private string s1;

    public Class1()
    {
        // no issue here..
        RefEater(ref s1);

        // Error CS0165 Use of unassigned local variable 's2'
        //string s2;
        //RefEater(ref s2);
    }

    private void RefEater(ref string s)
    {

    }
}


  • s1はデフォルトでnullで初期化されます。フィールドはデフォルト値で初期化され、参照型のデフォルト値はnullであるためです。なぜそれが問題を引き起こすことは決してないのです。 - Ankit Rana

-3

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

        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が必要です。それ以外の場合は、参照を渡していません。

私が知っている限りでは、stringはそれを装う参照型なので値型ではないので、あなたは構造体/値型と文字列それ自身のためのrefだけが必要です。

私はここで完全に間違っている可能性があります、私は新しいです。


  • Edwin、Stack Overflowへようこそ。私の知る限り、文字列は他のオブジェクトと同じように参照渡しです。文字列は不変のオブジェクトなので混乱しているかもしれませんが、参照によって渡されることはそれほど明白ではありません。その文字列がというメソッドを持っていたと想像してくださいCapitalize()それは文字列の内容を大文字に変えるでしょう。あなたがその後あなたのラインを交換したa = "testing";a.Capitalize();そうすれば、あなたの出力は「こんにちは」ではなく「こんにちは」となります。不変型の利点の1つは、参照を渡して他のコードが値を変更することを心配しなくても済むことです。 - Don Kirkby
  • 型が公開することができる意味論には3つの基本的なタイプがあります:可変の参照の意味論、可変の値の意味論、そして不変の意味論です。フィールドまたはプロパティmを持つ型Tの変数xとyを考え、xがyにコピーされると仮定します。 Tが参照セマンティクスを持つ場合、x.mへの変更はy.mによって観察されます。 Tが値意味を持つならば、y.mに影響を与えずにx.mを変更することができます。 Tが不変の意味を持つ場合、x.mもy.mも変更されません。不変のセマンティクスは、参照オブジェクトまたは値オブジェクトによってシミュレートできます。文字列は不変の参照オブジェクトです。 - supercat

-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;
    }

これは猫ではなく犬を書くでしょう。それ故、あなたは直接someObjectに取り組むべきです。


  • ここに記載されているすべてのことはほぼ真実ですが、値による参照間の違いを説明しているわけではありません。せいぜい、参照型と値型/不変型の違いは半分しか説明していません。 - Conrad Frix

リンクされた質問


関連する質問

最近の質問