C#の参照型と値型の違いは何ですか?


100

数か月前にこの質問をした人がいて、詳細に説明できませんでした。C#の参照型と値型の違いは何ですか?

私は、値の型があることを知っているintboolfloatなど、および参照型があるdelegateinterfaceなど、またはあまりにも、この間違っていますか?

それを専門的に説明していただけますか?


3
ちょっとしたメモとして、C#について質問されると思いますが、実際にはC#+ .NETについてです。.NETを分析せずにC#を分析することはできません。質問のタグを付け直しません。一方を分析せずにもう一方を分析する必要があるためです(イテレータとクロージャー、私はあなたを見ています)
xanatos '20

@xanatosこれは、C#、VB.Net、および.Netのすべてに共通するCLIに関する質問です。CLIのタグがあるはずですが、CLIは別の目的で使用されます。CLRがありますが、これは実装であり、標準ではありません。
user34660

回答:


172

あなたの例はしばらくので少し奇数でありintboolかつfloatある特定のタイプ、インターフェイスとデリゲートである種類のタイプの-同じようにstructenum値型の種類です。

この記事では、参照型と値型の説明を書きまし。私があなたが混乱していると思われるどんなビットでも展開したいです。

「TL; DR」バージョンは、特定のタイプの変数/式の値が何であるかを考えることです。値タイプの場合、値は情報そのものです。参照タイプの場合、値は参照であり、nullの場合もあれば、情報を含むオブジェクトに移動するための方法である場合もあります。

たとえば、変数を一枚の紙のように考えます。値「5」または「false」が書き込まれている可能性がありますが、私の家はありませんでした。私の家への道順がなければなりません。これらの方向は参照に相当します。特に、2人のユーザーが私の家への同じ方向を含む異なる紙片を持っている可能性があります。1人の人がその方向に従って私の家を赤く塗った場合、2人目の人もその変化を見るでしょう。2人とも私の家の写真を紙に書いていただけの場合、1人の人が紙を着色しても、もう1人の紙はまったく変わりません。


2
物事が提供できるセマンティクスの3つの異なる主要なタイプがあることに注意することが重要です:不変のセマンティクス、変更可能な値のセマンティクス、および変更可能な参照セマンティクス。概念的には、モノが実装するセマンティクスの種類は、スタンドアロンヒープオブジェクトとして格納されるか、変数/フィールド(構造体)として格納されるかに直交します。実際には、フィールドを公開しない構造体はあらゆる種類のセマンティクスを実装できますが、.netがヒープ参照の無差別共有を許可するという事実は、ヒープオブジェクトが可変値のセマンティクスを実装できないことを意味します。
スーパーキャット

このビットを取得できませんでした- while int, bool and float are specific types, interfaces and delegates are kinds of type - just like struct and enum are kinds of value types。intとはどういう意味ですか。boolは特定の型です。C#のすべて、たとえばint、bool、float、クラス、インターフェース、デリゲートはタイプ(正確にはデータタイプ)です。データ型は、C#では「参照型」と「値型」として分離されます。では、なぜintは特定の型ですが、インターフェイスは一種の型だとお考えですか?
RBT

2
@RBT:データ型は、「参照型」と「値型」に分離されるだけではありません。また、「クラス、構造体、列挙型、デリゲート、インターフェース」に分離されています。intは構造体、stringクラス、Actionデリゲートなどです。「int、bool、float、クラス、インターフェース、デリゲート」のリストは、「10、int」と同じように、さまざまな種類のものを含むリストです。さまざまな種類のものが含まれているリスト。
Jon Skeet

@JonSkeetおそらくこの投稿の答えは少し誤解を招く可能性があります。
RBT

@RBT:言葉遣いは少し悪いが、ひどいものではない。
Jon Skeet 2016

26

値のタイプ:

メモリアドレスではなく一部の値を保持します

例:

ストラクト

ストレージ:

TL; DR:変数の値は、それが明確にされている場所に保存されます。ローカル変数は、たとえば、スタックに住んでいるが、それはしっかりとそれがで宣言されたクラスと相まってヒープ上に住んでいるメンバーとしてクラス内で宣言するとき。
長いそれらが宣言されているところはどこでもこのように値型が格納されています。例:intローカル変数としての関数内のの値はスタックに格納され、intクラスのメンバーとして宣言されたin の値は、それが宣言されているクラスとともにヒープに格納されます。値の型クラスのライフタイプは、クラスが宣言されているクラスとまったく同じであり、ガベージコレクターによる作業はほとんど必要ありません。それはもっと複雑ですが、@ JonSkeetの本「C#In Depth」を参照しますより簡潔な説明のための「.NETのメモリ」。

利点:

値型は余分なガベージコレクションを必要としません。住んでいるインスタンスと一緒にガベージコレクションを取得します。メソッドのローカル変数は、メソッドの終了時にクリーンアップされます。

欠点:

  1. 値の大きなセットがメソッドに渡されると、受信変数が実際にコピーするため、メモリに2つの冗長な値があります。

  2. クラスが見落とされると、すべてのoopのメリットが失われます

参照タイプ:

値ではなく値のメモリアドレスを保持します

例:

クラス

ストレージ:

ヒープに格納

利点:

  1. 参照変数をメソッドに渡して変更すると、実際に元の値が変更されますが、値型では、指定された変数のコピーが取得され、その値が変更されます。

  2. 変数のサイズが大きい場合は参照型が良い

  3. クラスは参照型変数として提供されるため、再利用性を提供し、オブジェクト指向プログラミングにメリットをもたらします

欠点:

ガベージコレクターのvalue.extraオーバーロードを読み取るときの割り当てと逆参照時に参照する作業が増える


5
参照型がヒープに格納され、値型がスタックに格納されるとは限りません。詳細についてはyoda.arachsys.com / csharp / memory.htmlご覧ください。
Rhys

1
この回答には多くの誤解があります。C#を介してJeff Richters CLRをお読みください。値型はスレッドスタックに格納され、ガベージコレクション(GC)の対象ではありません。GCとは関係ありません。参照型はマネージヒープに格納されるため、GCの対象になります。参照タイプにルート参照がある場合、収集することはできず、世代0、1、2に昇格します。ルート参照がない場合、ガベージコレクトになる可能性があります。その後、Resurrectionと呼ばれるこのプロセスを実行します。殺されて生き返り、ついに回収される。
ジェレミー・トンプソン

13

コンピュータがどのようにメモリにデータを割り当てるかを知り、ポインタが何であるかを知っている方が、2つの違いを理解するのが簡単だと思いました。

参照は通常、ポインターに関連付けられています。変数が存在するメモリアドレスが実際には、実際のオブジェクトの別のメモリアドレスを別のメモリ位置に保持していることを意味します。

私がこれからしようとしている例は、非常に単純化されているため、1粒の塩でそれを取り上げます。

コンピューターのメモリが(POボックス0001からPOボックスnで始まる)一連の私書箱がその中に何かを保持できると想像してください。私書箱でうまくいかない場合は、ハッシュテーブルや辞書、配列などを試してください。

したがって、次のようなことをすると:

var a = "Hello";

コンピュータは次のことを行います。

  1. メモリを割り当て(たとえば、5バイトのメモリロケーション1000から開始)、H(1000で)、e(1001で)、l(1002で)、l(1003で)およびo(1004で)を置きます。
  2. メモリ内のどこかに(たとえば、位置0500に)割り当て、それを変数aとして割り当てます。
    つまり、エイリアスのようなものです(0500はaです)。
  3. そのメモリ位置(0500)の値を1000に割り当てます(これは、文字列Helloがメモリ内で始まる場所です)。したがって、変数aは、「Hello」文字列の実際の開始メモリ位置への参照を保持しています。

値タイプは、そのメモリ位置に実際のものを保持します。

したがって、次のようなことをすると:

変数a = 1;

コンピュータは次のことを行います。

  1. 0500で言うメモリ位置を割り当て、それを変数aに割り当てます(同じエイリアスのもの)
  2. 値1を(メモリ位置0500に)入れます。
    実際の値(1)を保持するために追加のメモリを割り当てていないことに注意してください。したがって、aは実際には実際の値を保持しているので、値型と呼ばれます。


@ジョン、まあ、そのようなことは私が言っていたものを無効にする、LOL。しかし、先ほど述べたように、2つのタイプをある程度理解することは非常に単純化されており、私の場合は役に立ちました。少なくともそれが私の心の中でそれを描いた方法です:)。
ジミーチャンドラ、

8

これは、約2年前の別のフォーラムからの投稿です。言語は(C#ではなく)vb.netですが、値型と参照型の概念は.net全体で統一されており、例はそのままです。

また、.net内では、すべてのタイプが技術的には基本タイプのオブジェクトから派生していることを覚えておくことも重要です。値タイプはそのように動作するように設計されていますが、最終的には基本タイプのオブジェクトの機能も継承します。

A.値タイプは、それだけです。それらは、個別のVALUEが格納されるメモリ内の別個の領域を表します。値タイプは固定メモリサイズであり、固定サイズのアドレスのコレクションであるスタックに格納されます。

このようなステートメントを作成すると、次のようになります。

Dim A as Integer
DIm B as Integer

A = 3
B = A 

次のことを行いました。

  1. 32ビット整数値を保持するのに十分なメモリ内に2つのスペースを作成しました。
  2. Aに割り当てられたメモリ割り当てに値3を配置
  3. Aに保持されているメモリと同じ値を割り当てることにより、Bに割り当てられたメモリ割り当てに値3を配置しました。

各変数の値は、各メモリ位置に個別に存在します。

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プロパティは、直感的に予想できるように、「ジムモリソン」も返します。私は、p1とp2の両方がスタック上の異なるアドレスを表していると思います。ただし、p2にp1の値を割り当てたので、p1とp2の両方がマネージヒープ上のSAME LOCATIONを指します。

この状況を考えてみましょう:

Dim p1 As clsPerson
Dim p2 As clsPerson

p1 = New clsPerson
p1.Name = "Jim Morrison"

p2 = p1

p2.Name = "Janis Joplin"

この状況では、オブジェクトを参照するスタック上のポインターp1を使用して、マネージヒープ上にpersonクラスの新しいインスタンスを1つ作成し、オブジェクトインスタンスのNameプロパティに「Jim Morrison」の値を再度割り当てました。次に、スタック内に別のポインターp2を作成し、p1が参照するものと同じマネージヒープ上の同じアドレスをポイントしました(割り当てp2 = p1を行ったとき)。

ひねりがここに来ます。p2のNameプロパティに値 "Janis Joplin"を割り当てると、次のコードを実行した場合に、p1とp2の両方で参照されるオブジェクトの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 = New clsPersonを設定するのはこのためです。

もう一度、今これを行うと:

p2.Name = "Jimi Hendrix"

MsgBox(p1.Name)
MsgBox(p2.Name)

両方のmsgBoxが「Jimi Hendrix」を返すようになりました

これは少し混乱する可能性があります。最後にもう一度言いますが、詳細の一部が間違っている可能性があります。

幸運、そしてうまくいけば、私よりもよく知っている他の人たちがこれを明らかにするのを手伝ってくれるでしょう。。。


なぜ賛成票が届かなかったのかわかりません。良い答えです。明確で単純な例で理解を助けました。
ハリー

.NET全体で均一である参照タイプの概念対値のタイプ、それらは実際に共通言語基盤(CLI)の仕様、Ecmaの標準335(また、ISO規格)で定義されています。これが.Netの標準部分の標準です。Ecma標準334(ISO標準でもあります)はC#言語であり、C#実装はCLIに依存するか、このC#標準で必要な最小限のCLI機能を利用する別の方法をサポートする必要があることを明示しています。VB.Netは標準ではありませんが、Microsoft独自のものです。
user34660

5

値のデータ型参照データ型

1) (データを直接含む)が参照 (データを 参照

2)における(すべての変数は独自のコピーを持っている)が、
基準(変数よりも、いくつかのオブジェクトを参照することができます)

3)(操作変数は他の変数に影響を与えることはできません)が参照(変数は他の変数に影響を与える可能性があります)

4) 値の型は(int、bool、float)ですが、 参照型は(array、class objects、string)です


2

値のタイプ:

  • 固定メモリサイズ。

  • スタックメモリに格納されます。

  • 実際の値を保持します。

    int、char、boolなど...

参照タイプ:

  • 固定メモリではありません。

  • ヒープメモリに格納されます。

  • 実際の値のメモリアドレスを保持します。

    文字列、配列、クラスなど...


1

「値タイプに基づく変数には直接値が含まれます。ある値タイプ変数を別の値タイプに割り当てると、含まれている値がコピーされます。これは、オブジェクトへの参照をコピーするがオブジェクト自体への参照をコピーしない参照タイプ変数の割り当てとは異なります。」マイクロソフトのライブラリから。

あなたはここここでより完全な答えを見つけることができます。


1
割り当ては参照型と値型で異なる動作をするように聞こえるので、その説明は好きではありません。そうではありません。どちらの場合も、「ターゲット」変数の値が式と等しくなります-値がコピーされます。違いは、その値が何であるかである-参照型のため、コピーされる値は、参照です。それでも変数の値は変わりません。
Jon Skeet

私はあなたに同意します、そしてあなたがこの記事で読むことができるように、それが異なる可能性があることをすでに知っています。しかし、私はこの件についてのMicrosoftのガイドと、あなたが通常本をどのように読んでいるかについての説明にパスしています。私を責めないでください!:)
Lucas

確かに...見つけられるべき欠陥があるMSDNドキュメントのビットがたくさんあります:)
Jon Skeet

1

説明は、特に初心者には特に役に立たない場合があります。値のタイプはデータファイルとして、参照タイプはファイルへのショートカットとして考えることができます。

したがって、参照変数をコピーする場合は、リンク/ポインタをメモリ内のどこかにある実際のデータにコピーするだけです。値型をコピーすると、実際にはメモリ内のデータのクローンが作成されます。


0

これはおそらく難解な方法で間違っていますが、簡単にするために:

値タイプは、通常「値によって」渡される値です(つまり、値をコピーします)。参照型は「参照によって」渡される(したがって、元の値へのポインタを与える)。.NET ECMA標準では、これらの「もの」がどこに保存されるかについての保証はありません。スタックレスまたはヒープレスの.NETの実装を構築できます(2番目は非常に複雑ですが、おそらくファイバーと多くのスタックを使用して可能です)。

構造体は値の型(int、bool ...は構造体、または少なくとも次のようにシミュレートされます)、クラスは参照型です。

値の型はSystem.ValueTypeから派生します。参照型はSystem.Objectから派生しています。

結局のところ、値の型、「参照オブジェクト」、および参照(C ++ではオブジェクトへのポインタと呼ばれます。.NETでは不透明です。それらが何であるかはわかりません。私たちの観点からすると、オブジェクトへの「ハンドル」です)。これらのラストは値タイプに似ています(コピーで渡されます)。したがって、オブジェクトは、オブジェクト(参照タイプ)とそれに対する0個以上の参照(値タイプと同様)で構成されます。参照がない場合、GCはおそらくそれを収集します。

一般に(.NETの「デフォルト」実装では)、値型はスタック(ローカルフィールドの場合)またはヒープ(クラスのフィールドの場合、イテレータ関数の変数の場合)に配置できます。それらがクロージャーによって参照される変数である場合、それらが非同期関数で(新しいAsync CTPを使用して)変数である場合...)参照値はヒープにのみ送信できます。参照は、値タイプと同じルールを使用します。

値型がイテレータ関数、非同期関数、またはクロージャによって参照されているためにヒープ上にある場合、コンパイルされたファイルを見ると、コンパイラがこれらの変数を配置するクラスを作成していることがわかります。 、関数を呼び出すとクラスが作成されます。

今、私は長いものを書く方法を知りません、そして私は私の人生の中でもっとやるべきことがありました。「正確な」「アカデミック」「正しい」バージョンが必要な場合は、次をお読みください。

http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

探してる15分です!"すぐに使える"記事の要約なので、msdnバージョンよりも優れています。


1
それは難解な方法以上に間違っています。これは根本的に間違っていると私は思います-参照型の値も値によって渡されるためです。値が参照であり、オブジェクトではないだけです。pobox.com/~skeet/csharp/parameters.htmlを参照してください。ああ、ローカル変数も、たとえばキャプチャされた場合やイテレータブロックの一部である場合など、ヒープ上で終了する可能性があります。
Jon Skeet

イテレータブロックはクラスに変換されるため、「背後」は「クラスのフィールド」です。クロージャについても同じです。ええ...私は「ポインター」(参照)と「先の尖った」の違いを書くのを忘れていました
xanatos

@xanatos:もちろん、これらはコンパイル後のクラスのフィールドですが、ソースコードではローカル変数のままです。また、参照自体を「値型」とは呼びません。あなたがどこから来たのかはわかっていると思いますが、このように水を濁すのは良い考えではないと思います。
Jon Skeet

@jonええ...ポインタは.netでは「不透明」であり、ValueTypeから派生しないため、3番目のタイプです。しかし、それらは参照よりも値型に似ています。それらを「参照」および「出力」できます。「誰か」がイテレータの働きをひっくり返す必要があったので、私は水を泥で汚さなければなりませんでした。
xanatos、2011

私が今指摘している記事を見ると、「(1)値型のインスタンス、(2)参照型のインスタンス、(3)参照の3種類の値があります。(C#のコードは操作できません。参照型のインスタンスを直接参照します。常に参照を介して行います。安全でないコードでは、ポインタ型は、値の格納要件を決定するために値型と同様に扱われます。))。
xanatos、2011

0

参照型を考える最も簡単な方法は、参照型を「オブジェクトID」と見なすことです。オブジェクトIDでできることは、1つを作成する、1つをコピーする、1つのタイプを照会または操作する、または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が黄色の車ではなく青い車を参照するようになったとしても、どちらもまだ同じ車である「オブジェクトID#19531」を参照しているため、ステートメントは実際にはXまたはYに影響しません。常にされています。


0

変数タイプと参照値は適用が簡単で、ドメインモデルに適切に適用できるため、開発プロセスが容易になります。

「値型」の量に関する神話を取り除くために、これがプラットフォームでどのように処理されるかについてコメントします。NET、具体的にはC#(CSharp)でAPISが呼び出されたときに、メソッドと関数で値によって、参照によってパラメーターを送信し、これらの値のパッセージを正しく処理する方法

この記事を読む Cでの変数の型の値と参照#


これは英語のみのQ&Aサイトです。残念ながら= \です。ただし、回答していただきありがとうございます。リンクは参考資料としてのみ(ただし、完全な持続的回答としてではなく)、完全な回答を作成してください。回答方法をご覧ください。
ジェシー

0

仮定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")が格納されます。参照型変数は、値が存在する比喩的なボックスへのリンクのみを格納します。


0

C#で使用できるさまざまなデータ型を説明する前に、C#は強く型付けされた言語であることを説明することが重要です。つまり、各変数、定数、入力パラメーター、戻り値の型、および一般に、値に評価されるすべての式には型があります。

各型には、コンパイラによって実行可能ファイルに埋め込まれるメタデータとして、共通言語ランタイム(CLR)が使用する情報が含まれています。これは、メモリを割り当てて再利用するときに型の安全性を保証します。

特定のタイプが割り当てるメモリの量を知りたい場合は、次のようにsizeof演算子を使用できます。

static void Main()
{
    var size = sizeof(int);
    Console.WriteLine($"int size:{size}");
    size = sizeof(bool);
    Console.WriteLine($"bool size:{size}");
    size = sizeof(double);
    Console.WriteLine($"double size:{size}");
    size = sizeof(char);
    Console.WriteLine($"char size:{size}");
}

出力には、各変数によって割り当てられたバイト数が表示されます。

int size:4
bool size:1
double size:8
char size:2

各タイプに関連する情報は次のとおりです。

  • 必要な保管スペース。
  • 最大値と最小値。たとえば、Int32型は2147483648〜2147483647の値を受け入れます。
  • 継承元の基本型。
  • 変数のメモリが実行時に割り当てられる場所。
  • 許可される操作の種類。
  • タイプに含まれるメンバー(メソッド、フィールド、イベントなど)。たとえば、int型の定義を確認すると、次の構造体とメンバーが見つかります。

    namespace System
    {
        [ComVisible(true)]
        public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>
        {      
            public const Int32 MaxValue = 2147483647;     
            public const Int32 MinValue = -2147483648;
            public static Int32 Parse(string s, NumberStyles style, IFormatProvider provider);    
            ... 
        }  
    }

メモリ管理 オペレーティングシステムで複数のプロセスが実行されていて、RAMの容量がそれをすべて保持するのに十分ではない場合、オペレーティングシステムはハードディスクの一部をRAMにマップし、ハードディスクへのデータの保存を開始します。オペレーティングシステムは、仮想アドレスが対応する物理アドレスにマップされている特定のテーブルよりも要求を実行するために使用します。メモリを管理するこの機能は、仮想メモリと呼ばれます。

各プロセスでは、使用可能な仮想メモリは次の6つのセクションにまとめられていますが、このトピックの関連性のために、スタックとヒープのみに焦点を当てます。

スタック スタックはLIFO(後入れ先出し)データ構造であり、サイズはオペレーティングシステムに依存します(デフォルトでは、ARM、x86、x64マシンの場合、Windowsは1MBを予約し、Linuxは2MBから8MBに応じて予約します。バージョン)。

メモリのこのセクションは、CPUによって自動的に管理されます。関数が新しい変数を宣言するたびに、コンパイラはスタックにそのサイズと同じ大きさの新しいメモリブロックを割り当て、関数が終了すると、変数のメモリブロックの割り当てが解除されます。

ヒープ このメモリ領域はCPUによって自動的に管理されず、そのサイズはスタックよりも大きくなります。新しいキーワードが呼び出されると、コンパイラは、要求のサイズに適合する最初の空きメモリブロックの検索を開始します。それが見つかると、組み込みのC関数malloc()を使用して予約済みとしてマークされ、その場所へのポインターが返されます。組み込みのC関数free()を使用して、メモリブロックの割り当てを解除することもできます。このメカニズムはメモリの断片化を引き起こし、ポインタを使用してメモリの正しいブロックにアクセスする必要があります。これは、スタックが読み取り/書き込み操作を実行するよりも低速です。

カスタム型と組み込み型 C#には、整数、ブール値、テキスト文字などを表す組み込み型の標準セットが用意されていますが、構造体、クラス、インターフェイス、列挙型などの構造を使用して、独自の型を作成できます。

構造体を使用したカスタムタイプの例は次のとおりです。

struct Point
{
    public int X;
    public int Y;
};

値と参照の種類 C#の種類は、次のカテゴリに分類できます。

  • 値のタイプ
  • 参照タイプ

値の型 値の型はSystem.ValueTypeクラスから派生し、この型の変数には、スタック内のメモリ割り当て内の値が含まれます。値タイプの2つのカテゴリーは、構造体と列挙型です。

次の例は、ブール型のメンバーを示しています。ご覧のとおり、System.ValueTypeクラスへの明示的な参照はありません。これは、このクラスが構造体によって継承されているために発生します。

namespace System
{
    [ComVisible(true)]
    public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean>
    {
        public static readonly string TrueString;
        public static readonly string FalseString;
        public static Boolean Parse(string value);
        ...
    }
}

参照型 一方、参照型には、変数に格納されている実際のデータは含まれていませんが、値が格納されているヒープのメモリアドレスが含まれています。参照型のカテゴリは、クラス、デリゲート、配列、およびインターフェイスです。

実行時に参照型変数が宣言されると、newキーワードを使用して作成されたオブジェクトがそれに割り当てられるまで、値nullが含まれます。

次の例は、ジェネリック型Listのメンバーを示しています。

namespace System.Collections.Generic
{
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(Generic.Mscorlib_CollectionDebugView<>))]
    [DefaultMember("Item")]
    public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
    {
        ...
        public T this[int index] { get; set; }
        public int Count { get; }
        public int Capacity { get; set; }
        public void Add(T item);
        public void AddRange(IEnumerable<T> collection);
        ...
    }
}

特定のオブジェクトのメモリアドレスを知りたい場合は、System.Runtime.InteropServicesクラスを使用すると、アンマネージメモリからマネージオブジェクトにアクセスできます。次の例では、静的メソッドGCHandle.Alloc()を使用してハンドルを文字列に割り当て、次にメソッドAddrOfPinnedObjectを使用してそのアドレスを取得します。

string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");

出力は

Memory address:39723832

参考文献 公式ドキュメント:https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019


-1

値の型と参照の型の違いについては、規格で明確に述べられている細かい点が数多くあり、特に初心者には理解しにくいものもあります。

ECMA標準33、Common Language Infrastructure(CLI)を参照してください。CLIもISOによって標準化されています。リファレンスを提供しますが、ECMAの場合はPDFをダウンロードする必要があり、そのリンクはバージョン番号によって異なります。ISO規格には費用がかかります。

1つの違いは、値の型はボックス化できますが、参照型は通常はボックス化できないことです。例外はありますが、かなり技術的なものです。

値型は、パラメータのないインスタンスコンストラクタやファイナライザを持つことはできず、それら自体を参照することもできません。自分自身を参照すると、値型が存在する場合に、例えば手段ノードその後のメンバーノードはできませんノード。仕様には他の要件/制限があると思いますが、そうであれば、それらは1つの場所にまとめられません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.