質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

Q&A

解決済

4回答

5522閲覧

C#:クラスのメンバーはメモリ上でどのような動きをしているのか

YuMo_tea

総合スコア17

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

0グッド

1クリップ

投稿2020/04/07 08:49

編集2020/04/07 08:50

前提・実現したいこと

以下のサンプルコード内でForm1.csをその他のクラスから操作する際、参照を渡す(先頭アドレス)という方法を取りました

参照を渡す→その他のクラスからForm1.csを操作できるようになる というところまでは理解できたのですが
Form1.cs内の関数(メソッド)や変数にアクセスできる理由がいまいち理解できませんでした

その中で、クラスのメンバーのメモリ管理について理解を深めれば今回の疑問は解決できるのではないかと思い、今回の質問に至ります

質問内容は、「試したこと・考えたこと」の未解決部分です

試したこと・考えたこと

以下のコードにおいて、各メンバーにアクセスできる理由を考えました

解決済 -------------------------------------------------------------------
・Form1objに実体のアドレスがあるため、Other_2.csからForm1にアクセスできる
・ヒープ領域で管理するものは、基本可変のもの
・値型の場合でも、メンバ変数の場合ヒープ領域に格納される

未解決 -------------------------------------------------------------------
・Form1として確保された領域内で、Form1の各メンバー領域をランダムに取っているのか?(画像上から3行目のメモリイメージ図)

*上記のように動くと仮定した場合*
・各メンバーにアクセスするためには、Form1のアドレスが先頭から末尾まで分かっていないと探しようがないのではないか?
・先頭アドレスだけで、Form1の各メンバーにアクセスできる仕組みが知りたい

イメージ説明

そもそも、解決済みの考えや、仮定の考えが間違ってるのではないか?と頭の中で無限ループしてしまっている状態です・・・

public void test() { Form1Obj.label1.AutoSize = true; Form1Obj.Form1_Method(); Form1Obj.A = 1; Form1Obj.B = true; Form1Obj.S = "あいうえお"; }

該当のソースコード

C#

1 public partial class Form1 : Form 2 { 3 4 private readonly Other _other; 5 public int A; 6 public bool B; 7 public string S; 8 9 10 public Form1() 11 { 12 InitializeComponent(); 13 14 //this:インスタンス自身(Form1)を格納する特別な変数 15 //otherインスタンスに対して自分の参照(先頭アドレス)を渡すことで、other内から自分(Form1)を操作できるようにするため 16 _other = new Other(this /*, 5)*/); 17 18 } 19 20 public void Form1_Method() 21 { 22   //何かの処理 23 } 24 25 } 26 27 class Other_2 //Form1を参照する 28 { 29 /// <summary> 30 /// Form1を示すメンバ変数(フィールド) 31 /// </summary> 32 public Form1 Form1Obj; 33 34 /// <summary> 35 /// コンストラクタでは、引数として渡された Form1型の変数 Form1Obj を自分(Other_2.cs)の★メンバ変数(フィールド) Form1Obj に格納している 36 /// </summary> 37 /// <param name="form1Obj"></param> 38 public Other_2(Form1 form1Obj) 39 { 40 //form1ObjはForm1の先頭アドレスを持っており、それを自分のメンバ変数(Form1Obj)に格納する 41 Form1Obj = form1Obj; 42 } 43 44 public void test() 45 { 46 Form1Obj.label1.AutoSize = true; 47 Form1Obj.Form1_Method(); 48 Form1Obj.A = 1; 49 Form1Obj.B = true; 50 Form1Obj.S = "あいうえお"; 51 } 52 }

補足情報(FW/ツールのバージョンなど)

.NET Framework4
Visual Studio 2010 Format Version 11.00

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2020/04/07 08:53

メソッドは第一引数を隠蔽された関数です。第一引数にはオブジェクトの参照が渡され、this という変数でアクセスできます。メソッドはインスタンスとは独立して管理されます。
gentaro

2020/04/07 09:01 編集

疑問を持つのはいいけど、C#って実メモリ上のアロケーションがどうなってるとかあまり意識せずに使えるように進化してきたものなので、今の方向性で勉強したいならC/C++あたりを勉強された方が捗る気がします。 C#の仕組みそのものに興味があるなら別にいいと思いますが。
YuMo_tea

2020/04/07 09:10

Zuishinさん 関数とメソッドは同じものという考えがあったので、目からウロコでした。 メソッドとインスタンスの関係についてもまだまだ理解不足だったので調べてみようと思います。 コメントありがとうございました。
YuMo_tea

2020/04/07 09:12

gentaroさん なるほどです。C#のメモリ関連は妙に記事が少ないなと思っていました。 意識しなくても使えるところがC#の良いところなんですね。CやC++はまったく未知の世界ですが、少し興味が出てきました。 コメントありがとうございました。
guest

回答4

0

前提

C#を利用する上でメモリがどのように管理されているかは基本的に意識する必要がありません。

パフォーマンスのチューニング、アンマネージドリソースのDispose漏れ、メモリリークなどを対応する場合は意識する必要があるかもしれませんが、基本的には必要ありません。

そして、C#を使う側の視点で考えることでC#の使い方を覚えるのが学習に有効と考えています。

その前提で回答しています。

回答

参照を渡す→その他のクラスからForm1.csを操作できるようになる というところまでは理解できたのですが

Form1.cs内の関数(メソッド)や変数にアクセスできる理由がいまいち理解できませんでした

Form1 に限らず、ABSForm1_Methodアクセス修飾子public だからです。

C#の振る舞いとしてはただただpublicなインスタンスであるForm1 にアクセス可能であり、Form1が持つABSForm1_Methodについてもpublic だからアクセス出来ているだけということになります。

・Form1として確保された領域内で、Form1の各メンバー領域をランダムに取っているのか?(画像上から3行目のメモリイメージ図)

*上記のように動くと仮定した場合*

・各メンバーにアクセスするためには、Form1のアドレスが先頭から末尾まで分かっていないと探しようがないのではないか?
・先頭アドレスだけで、Form1の各メンバーにアクセスできる仕組みが知りたい

C#では、メモリアドレスを考慮せずにオブジェクトを利用できます。なので、メンバーにアクセスできる理由についてはC#を使う側の視点で考えるとメモリアドレスとは無関係ということになります。

投稿2020/04/08 02:20

BluOxy

総合スコア2663

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YuMo_tea

2020/04/08 04:33

C#視点からの解説ありがとうございます。他の方のコメント等も見る限り、C#を使用するときにこの話を深く考えるのはあまり必要でないことがわかりました。(特に使用者の立場だと) 今までメモリ関連もよく考えないといけないのかなと思っていたので、使用者側では必要ないという意見をいただけて良かったです。 回答ありがとうございました。
guest

0

ベストアンサー

C#の実態とは多少ずれている可能性もありますが、静的型付けのオブジェクト指向言語でだいたい共通の大雑把なやり方を書いておきます。

まずインスタンスのメンバですが、Cの構造体と同じです。先頭アドレスから何バイト先の何バイトの領域はメンバaで……とかそんな感じで持ちます。

この位置関係はクラスごとに(つまり型ごとに)決まっています。属性アクセスを試みたとして、変数の型は静的型付けならわかるので、あとはアドレス演算でメンバの値を取り出せる訳です。というか、実際はコンパイル時にアドレス演算に変換してしまう訳です。

なお、メンバには値型と参照型の二種類があることに留意してください。値型はintなどで、普通オブジェクトの領域にそのまま埋め込まれます。参照型はポインタが埋め込まれ、この場合実体はまた別のメモリ領域に置かれます。たとえば文字列は長さが変わる可能性があり、値型としてしまうと上で書いた方法は破綻してしまいますが、参照型としておけばポインタの長さは一定(64bitとか)なので困りません。

ついでに書いておくと、あるクラスを継承した新しいクラスでメンバを追加したとします。親クラスにあったメンバのアドレス位置はそのままにしておき、新しいメンバをその後ろに付け加えれば、親クラスにある属性は親クラスの場合と同じ操作で取り出せる訳です。親クラスの型の変数に子クラスのインスタンスを代入できる理由がこれです。

次にメソッドですが、Hogeクラスのfugaメソッドがあったとして、Hoge_fuga関数みたいなものに変換しておきます(名前は説明のための便宜的なものですが)。この際、引数としてthisを渡すようにします。

Hogeクラスのインスタンスのhogeからhoge.fuga()されていたらHoge_fuga(hoge)に変換されます。ごく簡単に言うとそれだけです。


改めて書きますが、コンパイル時に変数の型がわかるからこういったことができるのです。動的型付け言語ではこういった方策は基本的には取れないため、別のやり方が必要になります(たとえば属性アクセスなら属性名でハッシュテーブルを探索して拾い上げる、とか)。

ただし昨今のトレンドとしては、動的型付け言語でも型推論や実行時コンパイルなどの技術を適用して内部的に静的に処理してパフォーマンスを稼ぐ、という方法も出てきています(JavaScriptなど)。この場合は、(実行されるまでの処理は色々と増えますが)本質的な実行のされ方は私の回答で書いたような静的型付けオブジェクト指向言語のやり方とさほど変わりありません。

(maisumakunさんからのコメントでのご指摘を受けて、上記2段落は投稿当初から修正・追記しています。)


CやJavaなどを学んだあと、この本(の特に後半)をやるとプログラミング言語の実装について理解が深まると思います。ただし敷居は高いです。

O'Reilly Japan - コンピュータシステムの理論と実装

投稿2020/04/07 10:53

編集2020/04/07 11:56
hayataka2049

総合スコア30935

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2020/04/07 11:35

> 動的型付け言語ではまったく違う方針で実装しているので注意が必要です JavaScriptでも型推論を行って、固定的な位置のアドレスにJITコンパイルしてしまうこともあるとのことです(もちろん変化したら作り直しになりますが)。
hayataka2049

2020/04/07 11:56

ご指摘ありがとうございます。回答に反映させていただきました。
len_souko

2020/04/07 12:04

> 敷居が高い https://dictionary.goo.ne.jp/word/%E6%95%B7%E5%B1%85%E3%81%8C%E9%AB%98%E3%81%84/ > 不義理や面目のないことがあって、その人の家へ行きにくい。 そもそも敷居は高くないです(高いと車いすの人はうちには入れてやらねぇぜって話になる) それを高いと感じるという比喩表現で、理由が「不義理を働いたから」という自責の念からくるものです 合わせる顔がないと同じような意味ですよね メモリの深いところを考えるのなら、正直プログラムの入門書にある変数は値を入れる箱って表現自体がNGだと思う Z80の機械語をちょっとだけ勉強したからコードも変数もすべてメモリに展開されてどのアドレスからどれだけの範囲をどの型の変数やロジック(関数など)として扱うって違いだけなのに、変数=値を入れる箱って説明のせいでメモリとは全く別の場所に存在する特別なものであるかのように錯覚してる人が多いような気がする なお、普段はC#しか触ってないので変な動きをしたとき以外はメモリは気にしてない
pepperleaf

2020/04/07 12:21 編集

> 参照型はポインタが埋め込まれ、 ポインタでしたか? 一種のインデックス(正式には?)で、実際のデータが置かれる場所は動的に変わるという認識ですが、、。そのため、C++などから、アクセスする場合、ロックが必要と。 また、うまく書けませんが、C#の場合、アドレスと言う概念で説明するには、ちょっと無理がありそうな気もします。(C には、アドレスが連続する等を利用した裏技があったりしましたが) 追記> 質問文に「参照を渡す(先頭アドレス)という」と記述がありました。ここで、既に認識が違う(アドレスじゃない)気がするのですが、うまく説明できない。
hayataka2049

2020/04/07 12:15 編集

本回答で記述している内容は「静的型付けのオブジェクト指向言語でだいたい共通の大雑把なやり方」ですので、特定言語の実装に強く依存する部分については皆様それぞれ回答していただけると有益な知見が積み上がるのではないかと思います。
pepperleaf

2020/04/07 12:15

len_soukoさん、 最近、C#がメインですが、メモリ(アドレス)を気にするのは、外部との連携の時だけ。Cも変な動きした時だけ。それで十分、使えました。さすがにアセンブラはメモリ配置が必須なので、、。高級言語を使うとき、そこまで考え出すと、メリット無いです。
len_souko

2020/04/07 12:41

そもそも、なんで気にしたんでしょうね 細かい挙動は下手したらバージョンが変わると変わる可能性もあるし、.NET(Core系の)ではCPUも色々サポートを増やすみたいだからプラットフォームによっても違ってくるかもしれないので気にする必要はないと思うんですけどねぇ
YuMo_tea

2020/04/08 01:09

hayataka2049さん 詳しい解説ありがとうございます。C#以外の言語について全然わからない状態で、このあたりの話に進むのはちょっとまずかったかもしれません・・・ですが、大体のイメージを掴むことが出来ました。C#にとどまらずもっと視野を広げて調べてみたいと思います。とても勉強になりました。 len_soukoさん 気にした経緯については、参照型と値型の違いを探る→メモリ管理の違いを見つける→参照型であるクラス内に、値型のメンバーがいたらどうなるのか?どこに配置されるの?というところからです。 正直気にしなくても良いというのはありましたが、ずっとひっかかっていてそこから今回の質問まで発展した次第です。 変数=箱の解説については、私も適切な表現ではないと思います。コメントありがとうございました。 pepperleafさん 以前、他クラスからForm1の操作方法を調べたときに、「参照型は代入だけでなく、メソッドに引数としてデータを渡した際にもコピー処理が行われる」という文章を見つけました。コンストラクタはメソッドの1種だと認識しているので、このコピー処理という概念がコンストラクタでも適用されると考えました。参照型のコピー方法は、実体を渡さず参照先(メモリアドレス、ポインタ?)を渡すなので、 >「参照を渡す(先頭アドレス)という」 という表現を使用しました。 C#はここまで考えなくても使用できるというのが良い点なんですね。あまり深みにハマりすぎないよう気を付けます。コメントありがとうございました。
len_souko

2020/04/08 11:14

> 気にした経緯については、参照型と値型の違いを探る→メモリ管理の違いを見つける→参照型であるクラス内に、値型のメンバーがいたらどうなるのか?どこに配置されるの?というところからです。 クラスをnewした際に確保されるメモリの内部には、参照型の場合ではポインタでしか確保できません 値型の場合は必要なサイズが確定しているのでそのサイズを確保できます もちろん、値型でもポインタを確保して、そこから別領域に必要サイズのメモリを確保してそのアドレスをポインタの値とすることも可能っちゃぁ可能ですが、二度手間になる(結果的に必要なメモリ領域がポインタのサイズ分だけ増えてメモリを読み込む処理が倍に増えるので遅くなります)ので多分そういう実装はされていないと思います C#でもunsafe以外でSpan<T>を使えばポインタ処理っぽいことができるらしい(僕は触ってないし今のところそこまでの速度を求める必要が出てくる処理にかかわったことがないです)ので、今までならVC++一択だった処理もいくらかはC#でできるようになるかもしれません そしたら今回の質問のようなレベルで考える必要が出てくるかもしれません
guest

0

Form1.cs内の関数(メソッド)や変数にアクセスできる理由がいまいち理解できませんでした

外部からアクセスできるように public で宣言したからです。メモリとか関係ありません。

投稿2020/04/08 01:19

Q71

総合スコア995

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YuMo_tea

2020/04/08 04:38

回答ありがとうございます。 私自身に「アクセス修飾子以外のことは関係ない」という視点が全く無く、とても参考になりました。
Q71

2020/04/09 21:43 編集

メモリ上の配置を気にしてコードを書く言語って何ですか? 訂正: コード > 言語
YuMo_tea

2020/04/10 00:25

私自身、他言語の知識がないため正確な回答では無いと思うのですが CやC++はメモリの配置を意識する機会が多い言語ではないか?と思います。(頂いたコメント、その他サイトなどを見る限り)
Zuishin

2020/04/10 00:32

C や C++ も初心者が VTABLE の実装や位置を気にしなければいけないようなことはほとんどありませんよ。public なら外からアクセスできるし、private ならできない、あとバッファをスタックに確保するかヒープに確保するか程度で十分です。 ただ、それらの言語をわざわざ使うのはカリカリにチューニングしたい時なので、そのような情報は比較的多くなりますね。
Q71

2020/04/10 03:37

なぜ、この質問に至ったのでしょう? object oriented language の勉強なら、アクセス指定子はいの一番に説明されています。Cでも、メモリの配置がどうのという話は、そうそう出てこないでしょう(初心者向けなら)。 どういう知識を持った人が、どの様な情報を見て、どう考えて質問に至ったのか、というのは、ある人々にとってはとても有用な情報です。できれば、提供いただきたい。
YuMo_tea

2020/04/10 05:09

Zuishinさん 補足ありがとうございます。参考にさせていただきます。 Q71さん 質問に至った経緯ということですね。 大雑把にまとめると、値型・参照型についてどんどん調べた結果、今回の疑問が浮かんできた・・・という流れになります。詳しい内容については下記ベストアンサー内のコメント欄に記載しているので、 よろしければそちらをご覧ください。 言語に詳しくないからこそ「メモリ関連は現段階で気にしなくても良い」という考えがそもそも思いつかなかったというのも原因の一つですが・・・
guest

0

・Form1として確保された領域内で、Form1の各メンバー領域をランダムに取っているのか?(画像上から3行目のメモリイメージ図)

この仮定は、ほぼ確実に正しくありません。性能などのことを考えれば、同じ型のオブジェクトは同じメモリレイアウトになるように処理されます。

投稿2020/04/07 09:03

maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YuMo_tea

2020/04/07 09:07

コメントありがとうございます。 同じメモリレイアウト、ということは並びで領域を確保していくということであっているでしょうか?
maisumakun

2020/04/07 09:09

> 並びで領域を確保していくということであっているでしょうか? 「同じ並びで」です。パディングなどがある場合もありますが、その位置も含めて全オブジェクトに共通します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問