連載C#入門第6回 変数を極めろ |
第1の答
|
|
第1の問題部分 |
まず25行目の出力を考えてみよう。21行目から25目までの流れを追えば、すべて理解できる。21行目から読みとれることは、int型つまり整数型の変数を2個宣言していると言うことだ。名前は、testInt1とtestInt2である。22行目でtestInt1には123という値が代入されている。そして、23行目で、testInt1の値をtestInt2に代入しているので、この時点で、2つの変数はどちらも123という値を持つ。24行目ではtestInt1に456が代入されているので、この時点では、testInt1は456、testInt2は123となる。25行目の実行結果を見れば、そのとおりであることが分かるだろう。これにはまったく謎はない。Visual Basic経験者もC経験者も納得する結果だと思う。これが最もシンプルな値型である。
第2の答
|
|
第2の問題部分 |
27〜33行目の結果は、実行結果の2行目に表示される。見て分かるとおり、今度はint型変数の代わりにクラスを使っている。といっても、TestClassクラスの宣言を見れば分かるとおり、このクラスが内部に持つのはやはりint型の整数型変数である。つまり、第1の答と第2の答の差は、クラスの外の整数型変数を使うか、クラス内部の整数型変数を使うかという違いと言うことができる。
クラスを使うため、27行目の変数宣言では、“int”ではなくクラス名である“TestClass”が記述されている。変数名としては、testClass1、testClass2を付けている。28〜29行目のコードは、第1の答にはないものだ。前出のクラス外の変数とは異なり、クラスは参照型であり、明示的に作成しなければ、その実体が作成されないために必要とされるものである。そこでnewキーワードを使ってインスタンスを作成して、それぞれ変数に代入している。
インスタンスのなかの変数を参照するには、インスタンスを格納した変数名の後にピリオド記号を置き、続けてクラス内の変数の名前を書く。30〜32行目の動作は、第1の答の22〜24行目とまったく同じである。結局のところ、第1の答と第2の答の違いが、int(整数)型変数がクラスのなかにあるか、外にあるかの差でしかないわけで、置かれた場所によってint(整数)型変数の挙動が変わる訳ではないので、結果は第1の答と同じである。
第3の答
ここまで来て、何だ簡単じゃないか、と思った人がいるかもしれない。だが、そろそろ理解が不完全だとミスしやすい世界に入って行くので十分注意していただきたい。
|
|
第3の問題部分 |
第2の答と第3の答の違いは、表面的には小さなものである。変数名の綴りを変えてあるが、これは機能とは何の関係もない変更である。“new”によるインスタンスの作成が1個しか行われていないが、それは38行目で別の変数の値が代入されようとしている変数に、あらかじめnewを用いて作成したインスタンスを代入しておく必要はない、と言うことから妥当であることは推測できるだろう。31行目と38行目の代入文がわずかに違うところが、最大の相違点と言える。第2の答(31行目)では、インスタンスのなかにある変数nの値をコピーしているが、第3の答(38行目)では、インスタンス全体を代入しているように見える。
一見、インスタンス全体が代入されるなら、中身の値も同時に代入されると考えて、第3の答も、第2の答とまったく同じ結果になると考えてしまいたくなる。ところが、31行目と38行目の代入文のほんの些細な記述の差が、結果を驚くほど変えてしまうのである。
先に結果を先取りして書くと、第3の答となる40行目の出力は、どちらも“456”という数値になる。第2の答では、“456”と“123”だったので、結果が違う。
いったい第2の答と第3の答では何が違うのか。C#プログラマとして、まず最初にピンと来なければならないのは、第2の答ではnewを2回使っているのに、第3の答では1回しか使っていないと言う事実である。これはインスタンスが1個しか作られていないと言うことを意味する。ということは、クラス内で宣言された変数nも1個しか作られていないことになる。その結果、確かにtestClass1aとtestClass2aという2つの変数が存在し、それぞれを経由してインスタンスを利用できるものの、実はどちらの変数を経由しても、実際に使われるのはたった1個インスタンスであったのだ。
代入してもたった1つの実体を指し示し続けると言うのが、参照型の大きな特徴である。クラスは参照型なので、インスタンスを指し示す変数を代入しても、ただ単に別名ができるだけで、データ本体の数が増えるわけではないのである。その結果、インスタンスのなかの変数に値を代入すると、そのインスタンスを参照するすべての変数から見える値が変わるのである。具体的に言えば、30行目の代入によって、testClass1a.nの値が変わるのはもちろん、一見関係なさそうに見えるtestClass2a.nの値も変わってしまうのである。
第2と第3の実行結果の違い |
第2の答(左)ではtestClass1、testClass2はそれぞれ別のインスタンスを参照しているが、第3の答(右)ではインスタンスの実体は1つしかなく、testClass1aとtestClass2aは同じインスタンスを参照している。 |
このように、代入しても代入しても、元の変数との縁が切れないのが参照型であるが、悪いことばかりではない。参照型とは言い換えれば、代入されても、複製を作らないで場所だけ伝達するデータ型と言うことになる。複製を作らないと言うことは、巨大なデータも素早く伝達させられるということである。
この違いを例え話で表現してみよう。巨大なファイルをたくさんの人に電子メールを用いて渡す必要が生じたとしよう。その際、方法は2つある。1つは、ストレートに巨大ファイルを添付ファイルとして送信する方法である。しかし、ただでさえ巨大なファイルを多人数に送るとなると、システムに大きな負荷がかかり時間もかかる。もう1つの方法は、そのファイルの入手先を記したURLだけを電子メールに書いて送る方法である。この方法ならシステムへの負荷はたいしたことはない。短いテキストを送るだけの手間ですむからだ。つまり、ファイルそのものを添付して送信するのが値型、URLだけを書いて送るのが参照型と考えると分かりやすいだろう。
INDEX | ||
C#入門 第6回 変数を極めろ | ||
1.理解度を確認しよう | ||
2.第1〜第3の答 | ||
3.第4〜第6の答 | ||
「C#入門」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|