数ヶ月前に、ある人がこの質問をしてくれたので、詳しく説明できませんでした。 C#の参照型と値型の違いは何ですか?
値型がint
、bool
、float
などであり、参照型がdelegate
、interface
などであることを知っています。これも間違っている?
専門的な方法で説明してもらえますか?
int
、bool
、およびfloat
は特定のタイプですが、インターフェースとデリゲートはkinds type-struct
やenum
が一種の値型であるように。
参照型と値型の説明anを書きました この記事では 。紛らわしい点があれば喜んで拡大します。
「TL; DR」バージョンは、特定のタイプの変数/式の値が何であるかを考えることです。値タイプの場合、値は情報そのものです。参照タイプの場合、値は参照であり、nullの場合もあれば、情報を含むオブジェクトにナビゲートする方法の場合もあります。
たとえば、変数は紙のようなものだと考えてください。値 "5"または "false"を書き込むことができますが、私の家を持つことはできません...私の家への方向が必要です。これらの指示は参照に相当します。特に、2人の人が私の家への同じ方向を含む異なる紙片を持っている可能性があります。1人がその指示に従って私の家を赤く塗った場合、2人目もその変化を見るでしょう。彼らの両方が紙の上に私の家の別々の写真を持っている場合、彼らの紙を着色する一人は他の人の紙を全く変えないでしょう。
メモリアドレスではなくいくつかの値を保持します
例:
構造
ストレージ:
TL; DR:変数の値は、明確にされていない場所に保存されます。たとえば、ローカル変数はスタック上に存在しますが、クラス内でメンバーとして宣言されると、宣言されたクラスと密に結合されたヒープ上に存在します。
Longer:したがって、値型は宣言された場所に保存されます。たとえば、ローカル変数としての関数内のint
の値はスタックに格納されますが、クラスのメンバーとして宣言されたint
の値はクラスitのヒープに格納されますクラスの値型には、宣言されているクラスとまったく同じライフタイプがあり、ガベージコレクタによる作業はほとんど必要ありません。しかし、より複雑です。より簡潔な説明については、@ JonSkeetの本 " C#In Depth "または彼の記事 " Memory in .NET "を参照してください。
利点:
値型には、余分なガベージコレクションは必要ありません。ガベージコレクションは、それが存在するインスタンスとともに収集されます。メソッドのローカル変数は、メソッドの終了時にクリーンアップされます。
欠点:
値の大きなセットがメソッドに渡されると、受信変数が実際にコピーされるため、メモリに2つの冗長な値があります。
クラスが見逃されると、すべてのOOPのメリットが失われます
値ではなく値のメモリアドレスを保持します
例:
クラス
ストレージ:
ヒープに保存
利点:
参照変数をメソッドに渡して変更すると、実際に元の値が変更されますが、値型では、指定された変数のコピーが取得され、その値が変更されます。
変数のサイズが大きい場合、参照型が良い
クラスは参照型変数として提供されるため、再利用性が得られるため、オブジェクト指向プログラミングに役立ちます
欠点:
ガベージコレクターのvalue.extraオーバーロードを読み取るときの割り当て時と参照解除時の参照の詳細な作業
コンピューターがメモリ内にデータを割り当てる方法と、ポインターとは何かを知っていれば、この2つの違いを理解するのが簡単だとわかりました。
参照は通常、ポインターに関連付けられています。変数が存在するメモリアドレスの意味は、実際には別のメモリ位置にある実際のオブジェクトの別のメモリアドレスを保持しています。
これから説明する例は、非常に単純化されているため、一粒の塩で取ります。
コンピューターのメモリが、POボックス0001からPOボックスnで始まる)連続したPOボックスであり、その中に何かを保持できると想像してください。私書箱があなたのためにそれをしないなら、ハッシュテーブルまたは辞書または配列または同様のものを試してください。
したがって、次のようなことを行う場合:
var a = "Hello";
コンピューターは次のことを行います。
値型は、メモリの場所に実際のものを保持します。
したがって、次のようなことを行う場合:
var a = 1;
コンピューターは次のことを行います。
これは、約2年前の別のフォーラムの私の投稿です。言語は(C#ではなく)vb.netですが、値型と参照型の概念は.net全体で統一されており、例は引き続き有効です。
また、.net内では、すべての型が技術的に基本型Objectから派生することを覚えておくことも重要です。値型はそのように動作するように設計されていますが、最終的には基本型Objectの機能も継承します。
A.値タイプは単なるものであり、個別のVALUEが保存されるメモリ内の個別の領域を表します。値タイプは固定メモリサイズであり、固定サイズのアドレスのコレクションであるスタックに格納されます。
このようなステートメントを作成する場合:
Dim A as Integer
DIm B as Integer
A = 3
B = A
次のことを行いました。
各変数の値は、各メモリ位置に個別に存在します。
B.参照タイプはさまざまなサイズにすることができます。したがって、それらを「スタック」に格納することはできません(スタックは、固定サイズのメモリ割り当てのコレクションですか?)。これらは「管理ヒープ」に保存されます。マネージヒープ上の各アイテムへのポインター(または「参照」)は、スタックに保持されます(アドレスのように)。コードは、スタック内のこれらのポインターを使用して、マネージヒープに格納されているオブジェクトにアクセスします。したがって、コードで参照変数を使用する場合、実際にはポインター(またはマネージヒープ内のメモリ位置への「アドレス」)を使用しています。
文字列Property Person.Nameを持つclsPersonという名前のクラスを作成したとします
この場合、次のようなステートメントを作成すると:
Dim p1 As clsPerson
p1 = New clsPerson
p1.Name = "Jim Morrison"
Dim p2 As Person
p2 = p1
上記の場合、p1.Nameプロパティは、予想どおり「Jim Morrison」を返します。また、p2.Nameプロパティは、「Jim Morrison」を返します。これは、直感的に予想されるとおりです。私は、p1とp2の両方がスタック上の異なるアドレスを表すと信じています。ただし、p2にp1の値を割り当てたため、p1とp2の両方が管理ヒープ上の同じ場所を指します。
この状況を理解してください:
Dim p1 As clsPerson
Dim p2 As clsPerson
p1 = New clsPerson
p1.Name = "Jim Morrison"
p2 = p1
p2.Name = "Janis Joplin"
この状況では、オブジェクトを参照するStack上のポインターp1を使用して、Managed Heapにpersonクラスの新しいインスタンスを1つ作成し、オブジェクトインスタンスのNameプロパティに「Jim Morrison」の値を再度割り当てました。次に、スタック内に別のポインターp2を作成し、p1によって参照されるものと同じマネージヒープ上のアドレスにポイントしました(割り当てをp2 = p1にしたとき)。
ここにひねりがあります。 p2のNameプロパティに値「Janis Joplin」を割り当てると、次のコードを実行した場合、p1とp2の両方によってREFERENCEDオブジェクトのNameプロパティが変更されます。
MsgBox(P1.Name)
'Will return "Janis Joplin"
MsgBox(p2.Name)
'will ALSO return "Janis Joplin"Because both variables (Pointers on the Stack) reference the SAME OBJECT in memory (an Address on the Managed Heap).
それは理にかなっていますか?
最終。これを行う場合:
DIm p1 As New clsPerson
Dim p2 As New clsPerson
p1.Name = "Jim Morrison"
p2.Name = "Janis Joplin"
これで、2つの異なるPersonオブジェクトができました。ただし、これをもう一度行うと:
p2 = p1
あなたは今、両方を「ジム・モリソン」に向けています。 (p2で参照されるヒープ上のオブジェクトに何が起こったのか、正確にはわかりません。今では範囲外になっていると思います。これは、誰かが私をまっすぐに設定できる領域の1つです。) -編集:これが、p2 = Nothing OR p2 =新しい割り当てを行う前に新しいclsPersonを設定する理由です。
もう一度、今これを行う場合:
p2.Name = "Jimi Hendrix"
MsgBox(p1.Name)
MsgBox(p2.Name)
両方のmsgBoxesが「Jimi Hendrix」を返すようになりました
これは少し混乱する可能性があります。最後にもう一度言いますが、詳細の一部が間違っている可能性があります。
幸運、そしてうまくいけば私よりもよく知っている他の人たちも一緒に来て、このことのいくつかを明らかにする手助けをします。 。 。
値のデータ型および参照データ型
1)値(データを直接含む)が参照(データを参照)
2)in value(すべての変数には独自のコピーがあります)
in 参照(変数がいくつかのオブジェクトを参照できる以上)
3)in value(操作変数は他の変数に影響を与えることはできません)、-reference(変数は他の変数に影響を与えることができます)
4)値の型 are(int、bool、float)しかし参照型は(array、class objects、string)
説明は特に初心者には役に立たないことがあります。値タイプをデータファイルとして、参照タイプをファイルへのショートカットとして想像できます。
そのため、参照変数をコピーする場合、メモリ内のどこかにある実際のデータにのみリンク/ポインターをコピーします。値型をコピーすると、実際にメモリ内のデータのクローンが作成されます。
値型と参照型の間に単一の違いはありません。標準によって明示的に述べられている多くの小さな詳細があり、それらの一部は、特に初心者にとっては理解しにくいものです。
[〜#〜] ecma [〜#〜] standard 33、Common Language Infrastructure(CLI)を参照してください。 CLIはISOによっても標準化されています。参照を提供しますが、ECMAの場合はPDFをダウンロードする必要があり、そのリンクはバージョン番号に依存します。ISO標準には費用がかかります。
1つの違いは、値型をボックス化できますが、参照型は通常ボックス化できないことです。例外もありますが、かなり技術的です。
値型には、パラメーターなしのインスタンスコンストラクターまたはファイナライザーを含めることはできず、それら自体を参照することはできません。自分自身を参照するということは、たとえば、値タイプNodeがある場合、NodeはNodeにはできません。仕様には他の要件/制限があると思いますが、その場合、それらは1か所に集められません。
これはおそらく密教的には間違っていますが、簡単にするために:
値タイプは、通常「値で」渡される値です(つまり、コピーします)。参照タイプは「参照渡し」で渡されます(そのため、元の値へのポインターが与えられます)。これらの「もの」が保存される場所の.NET ECMA規格による保証はありません。スタックレスまたはヒープレスの.NETの実装を構築できます(2番目は非常に複雑になりますが、ファイバーと多くのスタックを使用するとおそらく可能です)。
構造体は値型(int、bool ...は構造体、または少なくとも...としてシミュレートされます)、クラスは参照型です。
値型はSystem.ValueTypeから派生します。参照型はSystem.Objectから派生します。
さて..最後に、値型、「参照オブジェクト」、および参照(C++ではオブジェクトへのポインターと呼ばれます。NETでは不透明です。それらが何であるかはわかりません。オブジェクトの「ハンドル」です)。これらの最後は、値の種類に似ています(コピーで渡されます)。したがって、オブジェクトは、オブジェクト(参照型)とそのオブジェクトへの0個以上の参照(値型に似ています)で構成されます。参照がゼロの場合、GCはおそらくそれを収集します。
一般的に(.NETの「デフォルト」実装)、Value型はスタック(ローカルフィールドの場合)またはヒープ(クラスのフィールドの場合、イテレータ関数の変数の場合)に配置できます。それらがクロージャによって参照される変数である場合、非同期関数で変数である場合(新しいAsync CTPを使用)...)。参照値はヒープにのみ移動できます。参照は、値タイプと同じルールを使用します。
反復関数、非同期関数、またはクロージャーによって参照されているためにヒープに移動するValue Typeの場合、コンパイルされたファイルを見ると、コンパイラーがこれらの変数を入れるクラスを作成したことがわかります。 、および関数を呼び出すとクラスが構築されます。
今、私は長いものを書く方法を知りません、そして、私は私の人生でより良いことをします。 「正確な」「学術的な」「正しい」バージョンが必要な場合は、こちらをお読みください。
http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx
探しているのは15分です!要約された「すぐに使える」記事なので、msdnバージョンよりも優れています。
v
が値型の式/変数であり、r
が参照型の式/変数であるとします
x = v
update(v) //x will not change value. x stores the old value of v
x = r
update(r) //x now refers to the updated r. x only stored a link to r,
//and r can change but the link to it doesn't .
そのため、値型変数には実際の値(5または「h」)が格納されます。参照型変数は、値が存在する比phor的なボックスへのリンクのみを保存します。
変数タイプと参照値は簡単に適用でき、ドメインモデルに適切に適用されるため、開発プロセスが容易になります。
「値型」の量に関する神話を取り除くために、これがプラットフォームでどのように処理されるかについてコメントします。 NET、特にC#(CSharp)でAPISを呼び出し、値、参照、メソッド、関数でパラメーターを送信する場合、およびこれらの値のパッセージを正しく処理する方法.
この記事を読むCでの変数型の値とリファレンス#
値のタイプ:
固定メモリサイズ。
スタックメモリに保存されます。
実際の値を保持します。
例 int、char、boolなど...
参照タイプ:
固定メモリではありません。
ヒープメモリに格納されます。
実際の値のメモリアドレスを保持します。
例文字列、配列、クラスなど...
簡単に言えば、値型は値によって渡され、参照型は参照(メモリアドレス)によって渡されます。
つまり、呼び出されたメソッド内の値型パラメーター(正式なパラメーター)に加えられた変更は、メソッドが呼び出された元の値(実際のパラメーター)に反映されません。
ただし、呼び出されたメソッド内の参照パラメーターに加えられた変更は、呼び出し元のメソッドで宣言された変数の変更に反映されます。
それは簡単な説明です。 こちら を参照して、値の型、参照型、および値の型と参照型の詳細を理解してください。
参照型を考える最も簡単な方法は、参照型を「オブジェクトID」と見なすことです。オブジェクトIDでできることは、作成、コピー、タイプの照会または操作、または2つの同等性の比較だけです。オブジェクトIDを使用して他の操作を実行しようとすると、そのIDによって参照されるオブジェクトを使用して、指定されたアクションを実行するための略記と見なされます。
型Carの2つの変数XとY(参照型)があるとします。 Yはたまたま「オブジェクトID#19531」を保持しています。 「X = Y」と言うと、Xに「オブジェクトID#19531」が保持されます。 XもYも車を持たないことに注意してください。 「オブジェクトID#19531」としても知られる自動車は、別の場所に保管されます。 YをXにコピーしたとき、ID番号をコピーするだけでした。ここで、X.Color = Colors.Blueと言うことにします。このようなステートメントは、「オブジェクトID#19531」を見つけて青くペイントする指示と見なされます。 XとYが黄色の車ではなく青い車を指すようになったとしても、ステートメントは実際にはXまたはYに影響を与えないことに注意してください。常にされています。