回答:
参照が渡されます。ただし、技術的に参照渡しされていません。これは微妙ですが、非常に重要な違いです。次のコードを検討してください。
void DoSomething(string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.WriteLine(strMain); // What gets printed?
}
ここで何が起きているかを理解するために知っておくべきことが3つあります。
strMain
ですが、参照渡しされません。これは参照型ですが、参照自体はvalueによって渡されます。ref
キーワードなしでパラメーターを渡す(パラメーターをカウントしないout
)ときは常に、値で何かを渡しました。つまり、参照を値渡ししているということです。参照型なので、参照のみがスタックにコピーされました。しかし、それはどういう意味ですか?
C#変数は、参照型または値型です。C#パラメータは、参照渡しまたは値渡しのいずれかです。ここでは用語が問題です。これらは同じように聞こえますが、違います。
ANYタイプのパラメーターを渡し、ref
キーワードを使用しない場合は、値で渡しています。値で渡した場合、実際に渡したのはコピーです。ただし、パラメーターが参照型の場合、コピーしたのは参照であり、それが指しているものではありません。
Main
メソッドの最初の行は次のとおりです。
string strMain = "main";
この行で2つのものを作成main
しました。メモリのどこかに値が格納された文字列と、strMain
それを指すと呼ばれる参照変数です。
DoSomething(strMain);
次に、その参照をに渡しDoSomething
ます。値で渡したため、コピーを作成しました。これは参照型であるため、文字列自体ではなく、参照をコピーしました。これで、メモリ内の同じ値をそれぞれ指す2つの参照があります。
これがDoSomething
メソッドの一番上です:
void DoSomething(string strLocal)
いいえref
キーワード、そうしないstrLocal
とstrMain
同じ値に指す2つの異なる参照です。再割り当てするとstrLocal
...
strLocal = "local";
...保存されている値は変更していません。呼び出された参照を受け取り、strLocal
それを新しいストリングに向けました。何が起こるまでstrMain
、我々はそれを行うとき?何もない。それはまだ古い文字列を指しています。
string strMain = "main"; // Store a string, create a reference to it
DoSomething(strMain); // Reference gets copied, copy gets re-pointed
Console.WriteLine(strMain); // The original string is still "main"
シナリオを少し変えましょう。文字列ではなく、作成したクラスなどの変更可能な参照型を処理しているとします。
class MutableThing
{
public int ChangeMe { get; set; }
}
あなたがいる場合に従う参照objLocal
それが指すオブジェクトには、そのプロパティを変更することができます。
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
まだMutableThing
メモリには1つしかなく、コピーされた参照と元の参照の両方がまだそれを指しています。自身のプロパティMutableThing
が変更されました:
void Main()
{
var objMain = new MutableThing();
objMain.ChangeMe = 5;
Console.WriteLine(objMain.ChangeMe); // it's 5 on objMain
DoSomething(objMain); // now it's 0 on objLocal
Console.WriteLine(objMain.ChangeMe); // it's also 0 on objMain
}
ああ、でも文字列は不変です!ChangeMe
設定するプロパティはありません。strLocal[3] = 'H'
Cスタイルのchar
配列の場合のようにC#で行うことはできません。代わりに、まったく新しい文字列を作成する必要があります。変更する唯一の方法strLocal
は、参照を別の文字列にポイントするstrLocal
ことstrMain
です。これは、ユーザーが何も影響を与えないことを意味します。値は不変であり、参照はコピーです。
違いがあることを証明するために、参照によって参照を渡すと次のようになります。
void DoSomethingByReference(ref string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomethingByReference(ref strMain);
Console.WriteLine(strMain); // Prints "local"
}
今回Main
は、スタックにコピーせずに参照を渡したため、文字列inは実際に変更されます。
そのため、文字列は参照型ですが、値で渡すと、呼び出し先で行われる処理が呼び出し元の文字列に影響を与えることはありません。ただし、これらは参照型であるため、渡したい場合に、文字列全体をメモリにコピーする必要はありません。
ref
キーワードが必要であることです。参照渡しが違いをもたらすことを証明するには、次のデモを参照してください。rextester.com
ref
キーワードは、ユーティリティを持って、私はちょうど1の理由を説明しようとしていたかもしれません C#で値によって参照型を渡すのだと思うと、参照渡し(と参照型を渡すの「伝統的な」(すなわちC)概念のように思えますC#での参照による参照は、参照を値によって参照に渡すようなものです。
Foo(string bar)
と考えることができFoo(char* bar)
、一方、がFoo(ref string bar)
なりFoo(char** bar)
(またはFoo(char*& bar)
またはFoo(string& bar)
C ++で)。確かに、それはあなたが毎日それをどう考えるべきかではありませんが、それは実際に私が最終的に内部で何が起こっているかを理解するのに役立ちました。
C#の文字列は不変の参照オブジェクトです。つまり、それらへの参照は(値によって)渡され、文字列が作成されると変更できなくなります。文字列の変更されたバージョン(サブストリング、トリミングされたバージョンなど)を生成するメソッドは、元の文字列の変更されたコピーを作成します。
文字列は特別な場合です。各インスタンスは不変です。文字列の値を変更すると、メモリ内に新しい文字列が割り当てられます。
つまり、参照のみが関数に渡されますが、文字列が編集されると、新しいインスタンスになり、古いインスタンスは変更されません。
Uri
(クラス)とGuid
(構造体)も特殊なケースです。System.String
他の不変の型...クラスまたは構造体の起源のどれよりも「値型」のようにどのように機能するかはわかりません。
Uri
&とは異なりGuid
、文字列リテラル値を文字列変数に割り当てるだけです。文字列int
は再割り当てされているように変更可能であるように見えますが、暗黙的にオブジェクトを作成しています- new
キーワードはありません。