C# での REF および OUT キーワードの参照渡しおよび値渡しでの使用
-
18-09-2019 - |
質問
これまでに私が理解していることは次のとおりです。
値渡し
値による受け渡しとは、引数のコピーが渡されることを意味します。そのコピーを変更しても、オリジナルは変更されません。
参照渡し
参照渡しとは、オリジナルへの参照が渡されることを意味します。リファレンスへの変更はオリジナルに影響を与えます。
REFキーワード
REF は、関数に入る前にオブジェクトが初期化されていることをコンパイラに伝えます。REF は値がすでに設定されていることを意味し、メソッドはそれを読み取って変更できます。REF は、入力と出力の 2 方向です。
アウトキーワード
OUT は、オブジェクトが関数内で初期化されることをコンパイラに指示します。OUT は値がまだ設定されていないことを意味するため、return を呼び出す前に設定する必要があります。OUTは一方通行、つまりアウトです。
質問
それでは、どのようなシナリオで ref および out キーワードの使用と、参照渡しまたは値渡しを組み合わせるでしょうか?例は非常に役立ちます。
大変助かりました。
解決
あなたはを決しての1つのパラメータにref
とout
を組み合わせます。どちらも「参照渡し」を意味します。
もちろん一つの方法では、REFパラメータとoutパラメータを組み合わせることができます。
ref
とout
の差は主にの意図するにあります。 REF信号の双方向データ転送は、アウト1方向を意味します。
しかし、意思のほか、C#コンパイラは明確な割り当てを追跡し、それが最も顕著な違いになります。また、出力パラメータの誤用(読み出し)を防止することができる。
void SetOne(out int x)
{
int y = x + 1; // error, 'x' not definitely assigned.
x = 1; // mandatory to assign something
}
void AddTwo(ref int x)
{
x = x + 2; // OK, x is known to be assigned
}
void Main()
{
int foo, bar;
SetOne(out foo); // OK, foo does not have to be assigned
AddTwo(ref foo); // OK, foo assigned by SetOne
AddTwo(ref bar); // error, bar is unassigned
}
他のヒント
大変助かりました
言葉を正しく慎重に使用することで、理解が深まります。
値による受け渡しとは、引数のコピーが渡されることを意味します。
はい、これはまさに正確です。
そのコピーを変更しても、オリジナルは変更されません。
ではない正確に。を注意深く区別することから始めます。 価値観 そして 変数. 。考慮する:
class Foo { public int x; }
...
void N()
{
Foo blah = new Foo();
blah.x = 0;
M(blah);
}
...
void M(Foo foo)
{
foo.x = 123; // changes blah.x
foo = null; // does not change blah
}
ここでの変数は x、blah、foo です。x はフィールド、まあローカル、foo は仮パラメータです。
ここでの値は、null、0、123、および Foo のインスタンスへの参照です。 その参照は値です。 この事実を理解することが重要です。
blah の値のコピーは、変数 blah の値を変数 foo にコピーすることによって渡されます。blah の値は Foo のインスタンスへの参照です。
M は、Foo への参照である blah の値のコピーを持っているため、変数 x の値を変更できます。M が foo の内容を null に変更しても、それは何も変わりません。foo には、blah の値のコピーが含まれています。
参照渡しとは、オリジナルへの参照が渡されることを意味します。
言葉遣いは慎重に選んでください。「オリジナル」とは何ですか?
これは、「参照渡しとは、変数である必要がある引数への参照が渡されることを意味します」と表現したほうがよいでしょう。これをより簡単に考えると、「参照渡しにより、パラメータが引数として渡された変数のエイリアスになる」ということになります。
リファレンスへの変更はオリジナルに影響を与えます。
パラメータと引数はお互いのエイリアスであるため、参照への変更が元のパラメータに影響を与えるわけではありません。参照はオリジナルです。両方ともです 同じ変数.
REF は、関数に入る前にオブジェクトが初期化されていることをコンパイラに伝えます。
「物体」には意味がない。「変数」という意味ですね。
「ref」は「変数が初期化されていることをコンパイラに伝え」ません。むしろ、「ref」はコンパイラに「コンパイラは、変数が初期化されていることを確認する必要があります」と指示します。それはかなり違います!
REF は値がすでに設定されていることを意味します。
いいえ、参照では次のことが必要です。 変数 はすでに設定されています。「価値を決める」ということはありません。
したがって、メソッドはそれを読み取り、変更できます。
ここで、「それ」とは「変数」を意味します。
REF は、入力と出力の 2 方向です。
正しい。
OUT は、オブジェクトが関数内で初期化されることをコンパイラに指示します。
「変数」を意味するために「オブジェクト」を使用するのはやめてください。まったく異なるものを混同するのをやめれば、物事をより明確に理解できるようになります。 変数はオブジェクトではありません。 変数は 保管場所 そのうちのいくつかは以下を含む可能性があります 価値観, 、それらの値の一部は次のようになります。 オブジェクトへの参照.
つまり、out は変数がメソッド内で初期化されることをコンパイラーに伝えますが、これは完全に正しくありません。メソッドが例外をスローしたり、メソッドが無限ループに陥ったりするケースを忘れています。これらも正当なシナリオです。
OUT は値がまだ設定されていないことを意味します。
繰り返しますが、「値」とは「変数」を意味します。しかし、これは正確ではありません。初期化された変数を「out」パラメータとして渡すことは完全に正当です。無意味ですが合法です。
したがって、return を呼び出す前に設定する必要があります。
「return」は呼び出されません。メソッドが呼び出されます。ただし、メソッドは通常に戻る前に変数に値を割り当てる必要があります。
OUTは一方通行、つまりアウトです。
右。
それでは、どのようなシナリオで ref キーワードと out キーワードを組み合わせて使用しますか?
そのようなシナリオはありません。
どちらの方向へのパスのダイナミクスも理解しています。一部のパラメーターのシナリオは次のとおりです。
ref int num
in/outパラメータの場合。関数 5月 その中の値を変更します。out int num
ref と同様ですが、関数は戻る前に値を代入する必要があります。
一般に、出力パラメータは、関数の戻り値が 1 つだけであるため (複合値である場合もあります)、関数が複数の値を返さなければならない場合に適しています。
一部の ADO.NET メソッドなどのデータ アクセス プロバイダーは、出力パラメーターを使用して情報を返すことがあります。一部のデータ アクセス方法は、in/out パラメーターを持つデータベース ストアド プロシージャを模倣します。
編集: 参照型の条件の 1 つはパラメータです。 ref StringBuilder word
または StringBuilder word
(値による) 同じように動作します。外部の文字列は影響を受けますが、その時点ではフォーカスがヒープ上の値ではなく参照であるため、基礎となる実装はわずかに異なる場合があります。
編集: C# の 'out' キーワードが期待どおりの動作をしないことを反映するためにこの回答を修正しました (たとえば、コンピューター サイエンスの意味での真の OUT パラメーター)。私は当初、「out」は値渡し OUT であると述べましたが、間違いであることが判明しました。
「out」と「ref」を一緒に使用することはできません。C# (NET Framework) には 3 つの呼び出し規則があります。
- キーワードなし = 値渡し (IN)
- 'out' キーワード = 呼び出し前に明確な割り当て要件のない参照渡し (REF)
- 'ref' キーワード = 呼び出し前に明確な割り当て要件を伴う参照渡し (REF)
C# には、真の OUT または IN-OUT パラメーター機能はありません。
C# の 'out' パラメーターが真の OUT パラメーターではないことを確認するには、次のコードを使用できます。
public class Test
{
Action _showValue;
public void Run()
{
string local = "Initial";
_showValue = () => { Console.WriteLine(local.ToString()); };
Console.WriteLine("Passing by value");
inMethod(local);
Console.WriteLine("Passing by reference with 'out' keyword");
outMethod(out local);
Console.WriteLine("Passing by reference with 'ref' keyword");
refMethod(ref local);
}
void inMethod(string arg)
{
_showValue();
arg = "IN";
_showValue();
}
void outMethod(out string arg)
{
_showValue();
arg = "OUT";
_showValue();
}
void refMethod(ref string arg)
{
_showValue();
arg = "REF";
_showValue();
}
}
出力は次のとおりです。
Passing by value
Initial
Initial
Passing by reference with 'out' keyword
Initial
OUT
Passing by reference with 'ref' keyword
OUT
REF
ご覧のとおり、「out」と「ref」の両方が実際には REF を渡します。唯一の違いは、コンパイラーが明確な割り当ての目的でそれらを処理する方法です。
あなたは複数の値を返す必要がメソッドを持っている場合OUT
キーワードを使用すると便利です。例えば、int.TryParse()
のような方法を見てみます。
REF
を使用すると、オブジェクトの明示のためのより多くのです。任意の非プリミティブメソッドに渡されたが、本質的にそれのための必要性の多くは、通常のマネージコードではありません参照渡しされていることを念頭に置い。 REFキーワードを宣言することによって、あなたは、パラメータの可能性が高いメソッドの本体で変更され、明示的にも、呼び出し元のコードでref
を追加する必要がなぜこのように、呼び出し元のコードは、このように(それを認識するべきであると述べています。
多分これはあなたを助けるます:
void Foo(Bar) {} // pass by value c# (if it's a value type ;))
void Foo(Bar) {} // c++
void Foo(Bar) {} // pass by reference c# (if it's a reference type ;))
void Foo(Bar&) {} // c++
void Foo(ref Bar) {} // c#
void Foo(Bar*) // c++
void Foo(out Bar) {} // c#
void Foo(Bar**) {} // c++ (the contents of *Bar needs to be filled up)
あなたは正しく読み込まれるために初期化された変数を渡す必要がありますので、あなたが読んで、他の関数によって書かれたくREF何かに渡します。
EDIT:例:
void mymethod(ref int a) {
a++;
}
あなたは他の関数によって書き込まれるようにしたいようOUT何かを渡すが、それは機能で読み取ることがないので、あなたはそれを初期化する必要はありません、だけ書かれています。
EDIT:例:
void mymethod2(out string a) {
a="hello";
}