1. ホーム
  2. .net

[解決済み] .NETで文字列はどのように渡されるのですか?

2022-07-04 07:32:26

質問

を渡すと string を関数に渡すとき、文字列の内容へのポインタが渡されるのでしょうか、それとも文字列全体が struct のようにスタックで関数に渡されるのでしょうか?

どのように解決するのですか?

参照は渡されますが、技術的には 参照で渡されます。 これは微妙なことですが、非常に重要な違いです。次のコードを考えてみましょう。

void DoSomething(string strLocal)
{
    strLocal = "local";
}
void Main()
{
    string strMain = "main";
    DoSomething(strMain);
    Console.WriteLine(strMain); // What gets printed?
}

ここで起こることを理解するために必要なことが3つあります。

  1. 文字列はC#の参照型です。
  2. また、文字列は不変なので、文字列を変更しているように見えることをしても、そうではありません。全く新しい文字列が作成され、参照がそれに向けられ、古いものは捨てられる。
  3. 文字列が参照型であるにもかかわらず strMain は参照渡しではありません。それは 参照型です。 ですが、参照そのものは 値で渡される . パラメータを渡すときはいつでも ref キーワードを使わずにパラメータを渡した場合 (ただし out パラメータを含まない)、値で何かを渡したことになります。

ということは、...参照を値で渡しているということでしょう。参照型なので、参照だけがスタックにコピーされました。しかし、どういうことでしょうか?

参照型を値で渡す。すでにやっていること

C#の変数は 参照型 または 値型 . C#のパラメータは 参照で渡される または 値で渡される . 用語が問題で、これらは同じもののように聞こえますが、そうではありません。

ANY型のパラメータを渡す場合、そのパラメータに ref キーワードを使用しない場合、それは値によって渡されます。値で渡した場合、実際に渡したのはコピーです。しかし、もしパラメータが参照型であったなら、コピーしたものは 参照です。 であり、それが何を指しているのかはわかりません。

の最初の行は以下の通りです。 Main メソッドの最初の行です。

string strMain = "main";

この行では2つのものを作成しています: 値を持つ文字列 main という参照変数が作成されました。 strMain がそれを指しています。

DoSomething(strMain);

ここで、その参照を DoSomething . 値で渡しているので、コピーを作成したことになります。これは参照型なので、文字列そのものではなく、参照をコピーしたことを意味します。これで2つの参照ができ、それぞれがメモリ内の同じ値を指しています。

着信側の内部

ここでは、先頭の DoSomething メソッドを使用しています。

void DoSomething(string strLocal)

いいえ ref キーワードがないので strLocalstrMain は同じ値を指している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;
} 

まだ1つしかない MutableThing はまだ一つしかなく、コピーされた参照と元の参照の両方がまだそれを指しています。 のプロパティは 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#ではC言語の char の配列のように、全く新しい文字列を構築する必要があります。唯一の変更方法は strLocal を変更する唯一の方法は、参照を別の文字列に向けることであり、それはつまり、あなたが strLocal 影響を与えることができる strMain . 値は不変であり、参照はコピーである。

参照による参照の受け渡し

違いがあることを証明するために、次のようなことが起こります。 を参照渡しする場合です。

void DoSomethingByReference(ref string strLocal)
{
    strLocal = "local";
}
void Main()
{
    string strMain = "main";
    DoSomethingByReference(ref strMain);
    Console.WriteLine(strMain);          // Prints "local"
}

このとき、文字列は Main の文字列は、スタック上にコピーすることなく参照を渡したため、本当に変更されてしまいます。

つまり、文字列が参照型であっても、値で渡すということは、呼び出し側で何が起ころうとも呼び出し側の文字列に影響を与えないということです。しかし、文字列は 参照型であるため、文字列を渡すときにメモリ内の文字列全体をコピーする必要はありません。

さらなるリソース