先のエントリで言及したけど、意識したことがなかったので、意識してみるエントリー。
Visual C# 2008 をインストールすると、インストールしたディレクトリの下(C:\Program Files\Microsoft Visual Studio 9.0\VC#\Specifications\1041)に、Word 形式の言語仕様があります。まず、これに尋ねます。
『C# 言語仕様 Ver.3.0』「4.3 ボックス化とボックス化解除」より:
ボックス化とボックス化解除は、C# の型システムの中心的な概念です。これは、value-type のすべての値と object
型間の変換を可能にするため、value-types と reference-types との橋渡しの役目を果たします。ボックス化とボックス化解除の機能により、型システムを統一的に見ることができるようになり、すべての型の値を最終的にオブジェクトとして扱うことができます。
なるほど。中心的な概念
なのだそうです。うむむ。知らなかった。。。
C# では、object 型は、全ての型の派生元であるとされています。派生元への変換は、暗黙の内に行えます。全ての型の派生元であるが故に、int や double などの値型からも暗黙の内に変換できなければなりません。この、値型から参照型への変換をしているのが、ボックス化、ってわけですね。
何が行われるかについては、「4.3.1 ボックス化変換」に書いてあります。オブジェクト インスタンスの割り当てと、値のコピーが行われます。値のコピーが行われます?値のコピーですって?!
はい、元のものとは別の、コピーされたものが作られます。コピーされるということは、コピーを書き換えても、オリジナルは書き換わらないということです。
ではこれ、どういうところで問題になるのでしょう?
と、、、思いつかないので(意識したことがないのだから、当たり前ですね)、ボックス化の問題ではないが、値型が関係する問題を紹介。
Windows アプリケーションを作ります。System.Windows.Forms.Form
クラスです。フォームの位置は、Forms.Location
プロパティで参照できます。Location は、System.Drawing.Point
を返します。この中には、X 座標と Y 座標が入っています。では、次のようなコードで、フォームの位置を変えることができるでしょうか。
... 省略して、何かのメソッドの中 ...
form1.Location.X += 100; // 位置を右へ100ずらす
... 後ろも省略 ...
わざわざ「問題を紹介」といっているのだから、変えることはできない、というのが正解です。なぜでしょう?
プロパティは、アクセス メソッドを扱いやすくしたものです。メソッドに分解すると、先のコードは、次のコードのようになります。
... 省略して、何かのメソッドの中 ...
System.Drawing.Point loc = form1.GetLocation();
loc.X += 100;
... 後ろも省略 ...
... おそらく、Form.GetLocation() は、次のように実装されている ...
Point GetLocation()
{
return this.location;
}
ここで、Point が値型であることが重要です。Point は値型なので、GetLocation が返す値は、呼び出し元へコピーされます。コピーなので、いくら loc の値を変化させても、Form が持っている location の値は変化しません。変化させるためには、値型のプロパティそのものを書き換える必要があります。
... 省略して、何かのメソッドの中 ...
Point loc = form1.Location;
loc.X += 100; // ここでは変化しない
form1.Location = loc; // ここで変わる
loc.X -= 100; // セッターでもコピーするので、これは変わらない
... 後ろも省略 ...
これは、プロパティへのアクセスで値型の値渡しが行われ、値がコピーされることにより発生します。繰り返しますが、この例はボックス化による問題ではありません。しかし、ボックス化では、オブジェクト インスタンスの割り当てと、値のコピーが行われるため、ボックス化したときに元の値型変数が持つ値にアクセスすることはできなくなります。そのため、同じように、「値を変更したのに、変わってない」という問題が発生するでしょう。
で、ボックス化が、どういうときに問題になるか。。。例えば、「変更することができる」というインターフェイスを作成します。このインターフェイスを実装する値型をつくります。「変更することができる」なので、変更をするメソッドを作ります。このとき、「変更する」メソッドを使う方は、インターフェイスからアクセスするでしょう。そうすると、値型をインターフェイスにキャストしたときに、実はボックス化が行われるので、「変更する」メソッドではボックス化された値型にアクセスすることになります。すると、これはボックス化の過程でコピーが行われているため、本来の値は変更されないことになります。
投稿日時 : 2009年6月3日 22:35