関数でオブジェクトを変更できるように、オブジェクトを渡す必要がある場所に関数を作成しています。違いは何ですか:
public void myFunction(ref MyClass someClass)
そして
public void myFunction(out MyClass someClass)
どちらを使用すればよいですか、またその理由は何ですか。
ref
関数に入る前にオブジェクトが初期化されていることをコンパイラに伝えます。out
オブジェクトが関数内で初期化されることをコンパイラーに指示します。
そうながらref
双方向ですout
アウトのみです。
のref
修飾子は、
のout
修飾子は、
out
メソッドが呼び出される前に初期化されている場合、そのメソッドによって設定される前に、メソッド内で完全に読み取ることができますか。つまり、呼び出されたメソッドは、呼び出し元のメソッドが引数として渡したものを読み取ることができますか? - Panzercrisis
DomがTPSレポートに関するメモについてPeterのブースに登場したとしましょう。
もしDomがrefの議論であれば、彼はそのメモの印刷されたコピーを持っているでしょう。
もしDomが議論の余地がなかったら、彼は彼が彼と一緒に持っていくために彼にそのメモの新しいコピーを印刷させるでしょう。
私は説明を試みるつもりです:
値型がどのように正しく機能するのか理解していると思いますか。値の型は(int、long、structなど)です。 refコマンドを使わずに関数にそれらを送ると、データ。関数内のそのデータにあなたがしたことは、元のコピーではなくコピーにのみ影響します。 refコマンドはACTUALデータを送信し、変更があると関数外のデータに影響します。
わかりにくい部分は、参照型です。
参照型を作成しましょう。
List<string> someobject = new List<string>()
あなたが新しいとき誰か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
ref
そしてout
パラメーター。 - Joyce Babuout
キーワード? - Asif Mushtaq
あなたが使うべきですout
それがあなたの要求に十分であるところはどこでも優先して。
C#では、メソッドは1つの値しか返すことができません。複数の値を返したい場合は、outキーワードを使用できます。 out修飾子は、参照による戻り値として返されます。最も単純な答えは、メソッドから値を取得するためにキーワード「out」が使用されることです。
C#では、int、float、doubleなどの値型をメソッドパラメータの引数として渡すと、値によって渡されます。したがって、パラメータ値を変更しても、メソッド呼び出しの引数には影響しません。しかし、もしあなたが“ ref”キーワードでパラメータをマークすると、それは実際の変数に反映されます。
犬、猫の例を拡張する。 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;
}
参照型(クラス)を渡しているので、使用する必要はありません。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
メソッド内のオブジェクトを変更したい場合
ref
そしてout
以下の違いを除いて同様に動作します。
ref
変数は使用前に初期化する必要があります。out
変数は代入なしで使用できますout
parameterは、それを使用する関数によって未割り当ての値として扱われる必要があります。だから、私たちは初期化を使用することができますout
呼び出し元のコードにはパラメータがありますが、関数を実行すると値は失われます。
「パン屋」
これは最初のものがあなたの文字列参照を "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クラスを使用するとパフォーマンスが向上することがよくあります)
例によって(私のように)学ぶ人のためにここに何があるかAnthony Kolesovが言っています。
この点を説明するために、ref、outなどの最小限の例をいくつか作成しました。ベストプラクティスは扱っていません。違いを理解するための例にすぎません。
でる:関数から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();
}
}
}
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
参照refパラメータの値がすでに設定されていることを意味し、メソッドはそれを読み取って変更できます。 refキーワードを使用することは、呼び出し側がパラメータの値を初期化する責任があるということと同じです。
でるobjectの初期化はの責任であることをコンパイラに伝えます 関数、関数はoutパラメータに割り当てる必要があります。 未割り当てのままにすることはできません。
オーサリング時間:
(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)を続けます。初期化されていないエラーはスローされず、以前に保存された参照はすべて失われます。
それらはほとんど同じです - 唯一の違いは、outパラメータとして渡す変数は初期化する必要がないということです、そしてrefパラメータを使うメソッドはそれを何かに設定しなければならないということです。
int x; Foo(out x); // OK
int y; Foo(ref y); // Error
refパラメータは変更される可能性のあるデータ用で、outパラメータはすでに何かの戻り値を使用している関数の追加出力であるデータ用です(例:int.TryParse)。
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を初期化しなければなりません
参照: 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は両者を区別しません)。
以下に両方を使った例を示しました参考そしてでる。今、あなたはすべて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; }
}
パラメータを受け取るメソッドの観点からは、ref
そしてout
C#はすべてのメソッドに書き込む必要があるということです。out
パラメータを返す前に、そのようなパラメータに対して何もしてはいけません。out
パラメータとして渡されるか、パラメータとして書き込まれるout
別のメソッドへのパラメータ、または直接書き込まれます。他のいくつかの言語はそのような要求を課さないことに注意してください。 C#で宣言された仮想メソッドまたはインタフェースメソッドout
パラメータは、そのようなパラメータに特別な制限を課さない別の言語で上書きされる可能性があります。
呼び出し側から見れば、多くの状況でC#は、out
parameterを指定すると、渡された変数は最初に読み取られることなく書き込まれます。他の言語で書かれたメソッドを呼び出すとき、この仮定は正しくないかもしれません。例えば:
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で書かれたメソッドを呼び出すときに起こり得ます。
パラメータを参照として渡したい場合は、パラメータを関数に渡す前に初期化する必要があります。それ以外の場合は、コンパイラ自体がエラーを表示します。ただし、outパラメータの場合は、オブジェクトパラメータを初期化する必要はありません。 method.呼び出し元のメソッド自体でオブジェクトを初期化できます。
私はと遊んでいた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)
{
}
}
あまり得意ではないかもしれませんが、文字列は(技術的には参照型でヒープ上に存在していますが)確かに参照ではなく値で渡されますか。
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だけが必要です。
私はここで完全に間違っている可能性があります、私は新しいです。
Capitalize()
それは文字列の内容を大文字に変えるでしょう。あなたがその後あなたのラインを交換したa = "testing";
とa.Capitalize();
そうすれば、あなたの出力は「こんにちは」ではなく「こんにちは」となります。不変型の利点の1つは、参照を渡して他のコードが値を変更することを心配しなくても済むことです。 - Don Kirkby
関数内で渡される参照パラメータが直接処理されるように注意してください。
例えば、
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に取り組むべきです。
MyClass
になりますclass
type、つまり参照型。その場合、渡したオブジェクトは次のように変更できます。myFunction
いいえでもref
/out
キーワード。myFunction
を受け取ります新しいを指す参照同じそして、同じオブジェクトを必要なだけ変更できます。違いはref
キーワードは作るだろう、ということmyFunction
を受け取りました同じ同じオブジェクトへの参照それが重要なのはmyFunction
への参照を変更することでした別のオブジェクト - Jeppe Stig Nielsen