関数によって変更できるように、オブジェクトを渡す必要がある関数を作成しています。違いは何ですか:
public void myFunction(ref MyClass someClass)
と
public void myFunction(out MyClass someClass)
どちらを使用する必要があり、その理由は?
ref
関数に入る前にオブジェクトが初期化out
されることをコンパイラーに伝え、関数内でオブジェクトが初期化されることをコンパイラーに伝えます。
whileref
は双方向、out
out-only です。
ref
修飾子は次のことを意味します。
out
修飾子は次のことを意味します。
Dom が TPS レポートに関するメモについて Peter のキュービクルに現れたとします。
Dom が ref 引数である場合、彼はメモの印刷されたコピーを持っているでしょう。
もしドムが言い争ったら、彼はピーターにメモの新しいコピーを印刷させて、彼が持っていくだろう.
説明を試してみます。
値の型がどのように機能するかを理解できたと思いますか? 値の型は (int、long、struct など) です。それらを ref コマンドなしで関数に送信すると、データがコピーされます。関数でそのデータに対して行うことはすべて、元ではなくコピーにのみ影響します。ref コマンドは ACTUAL データを送信し、変更は関数外のデータに影響します。
わかりにくい部分、参照型に進みます。
参照型を作成しましょう:
List<string> someobject = new List<string>()
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
あるオブジェクトを ref でメソッドに送るとどうなるでしょうか? 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 はインとアウトです。
out
要件を満たすのに十分な場合は、優先して使用する必要があります。
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;
}
refは、ref パラメーターの値が既に設定されていることを意味し、メソッドはそれを読み取って変更できます。ref キーワードを使用することは、呼び出し元がパラメーターの値を初期化する責任があると言っているのと同じです。
outは、オブジェクトの初期化は関数の責任であり、関数は out パラメータに割り当てる必要があることをコンパイラに伝えます。未割り当てのままにすることはできません。
(私のように) 例によって学ぶ人のために、Anthony Kolesov が言っていることを次に示します。
ポイントを説明するために、ref、out、およびその他の最小限の例をいくつか作成しました。ここではベスト プラクティスについて説明するのではなく、違いを理解するための例にすぎません。
参照型 (クラス) を渡すので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
、メソッド内のオブジェクトを変更する場合に使用する必要はありません。
「ベイカー」
これは、最初の文字列参照が "Baker" を指すように変更されるためです。ref キーワード (=> 文字列への参照への参照) を介して渡したため、参照を変更することができます。2 番目の呼び出しは、文字列への参照のコピーを取得します。
string は、最初は特別に見えます。ただし、文字列は単なる参照クラスであり、定義した場合
string s = "Able";
s は、テキスト「Able」を含む文字列クラスへの参照です! を介した同じ変数への別の割り当て
s = "Baker";
元の文字列を変更するのではなく、単に新しいインスタンスを作成し、s がそのインスタンスを指すようにします!
次の小さなコード例で試すことができます。
string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);
あなたは何を期待していますか?s2 が元のインスタンスを指している間に s の参照を別のインスタンスに設定するだけなので、得られるのは「Able」です。
編集: 文字列も不変です。つまり、既存の文字列インスタンスを変更するメソッドやプロパティがまったくないことを意味します (ドキュメントで見つけようとすることはできますが、何も見つけることはできません :-) )。すべての文字列操作メソッドは、新しい文字列インスタンスを返します! (そのため、StringBuilder クラスを使用するとパフォーマンスが向上することがよくあります)
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();
}
}
}
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 パラメーターとして渡す変数を初期化する必要がなく、ref パラメーターを使用するメソッドがそれを何かに設定する必要があることです。
int x; Foo(out x); // OK
int y; Foo(ref y); // Error
ref パラメーターは、変更される可能性のあるデータ用であり、out パラメーターは、何かの戻り値を既に使用している関数 (int.TryParse など) の追加出力であるデータ用です。
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 の両方を同時に使用することはできません。ただし、ref と out は実行時には異なる方法で処理されますが、コンパイル時には同じように処理されます (CLR は ref と out の IL を作成する間、2 つを区別しません)。
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」を使用すると、そのint /文字列を初期化するかどうかに関係なく、両方の条件で機能しますが、その関数でそのint /文字列を初期化する必要があります
以下に、 Refとoutの両方を使用した例を示しました。これで、ref と out についてはすべてクリアされます。
以下の例では、コメント//myRefObj = new myClass { Name = "ref outside called!! " };をコメントします。 行、「割り当てられていないローカル変数 'myRefObj' の使用」というエラーが発生しますが、 outにはそのようなエラーはありません。
Ref を使用する場所: in パラメーターを使用してプロシージャーを呼び出し、そのプロシージャーの出力を格納するために同じパラメーターが使用される場合。
Out を使用する場所: in パラメータを指定せずにプロシージャを呼び出す場合、同じパラメータを使用してそのプロシージャから値を返します。また、出力に注意してください
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; }
}
関数内で渡される参照パラメーターが直接処理されることに注意してください。
例えば、
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 で直接作業する必要があります。
私はこれが得意ではないかもしれませんが、確かに文字列は (技術的には参照型であり、ヒープ上に存在しますが) 参照ではなく値で渡されますか?
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 が必要な理由です。それ以外の場合は参照を渡しません。
私が知る限り、構造体/値型と文字列自体の参照のみが必要です。文字列は、値型ではないふりをする参照型であるためです。
ここでは完全に間違っている可能性がありますが、私は新しいです。