C#では、クラスは参照型、構造体は値型という大きな違いがある・・・。
TestClass.csを、
// 名前空間の登録 using System; // 共通データ型と基本クラス(System名前空間) // 文字列を持つクラス class TestClass // TestClassクラスの定義 { // メンバフィールドの定義 private string str; // string型フィールドstr public string Str // strの設定と取得をするプロパティStr. { get { // strを返す. return str; } set { // strにvalueを格納. str = value; } } private int i; // int型フィールドi public int I // iの設定と取得をするプロパティI. { get { // iを返す. return i; } set { // iにvalueを格納. i = value; } } // iとstrを表示. public void Show() { Console.WriteLine("i = " + i + ", str = " + str); } }
TestStruct.csを、
// 名前空間の登録 using System; // 共通データ型と基本クラス(System名前空間) // 文字列を持つ構造体 struct TestStruct // TestStruct構造体の定義 { // メンバフィールドの定義 private string str; // string型フィールドstr public string Str // strの設定と取得をするプロパティStr. { get { // strを返す. return str; } set { // strにvalueを格納. str = value; } } private int i; // int型フィールドi public int I // iの設定と取得をするプロパティI. { get { // iを返す. return i; } set { // iにvalueを格納. i = value; } } // iとstrを表示. public void Show() { Console.WriteLine("i = " + i + ", str = " + str); } }
としておく・・・。
そして、MainClass.csでいろいろ試してみる・・・。
// 名前空間の登録 using System; // 共通データ型と基本クラス(System名前空間) // メインクラス class MainClass // MainClassクラスの定義 { // メインメソッド static void Main() // Mainメソッドの定義 { // オブジェクトの宣言 TestClass testClass; // TestClassオブジェクトtestClassの宣言 TestStruct testStruct; // TestStructオブジェクトtestStructの宣言 // nullかどうかを調べる. if (testClass == null) // testClassがnullなら. { Console.WriteLine("testClass == null!"); // "testClass == null!"と出力. } if (testStruct == null) // testStructがnullなら. { Console.WriteLine("testStruct == null!"); // "testStruct == null!"と出力. } } }
まず、こうしてみたら、
------ ビルド開始: プロジェクト: struct, 構成: Debug Any CPU ------ C:\Windows\Microsoft.NET\Framework\v2.0.50727\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:DEBUG;TRACE /debug+ /debug:full /optimize- /out:obj\Debug\struct.exe /target:exe MainClass.cs TestClass.cs TestStruct.cs C:\Project\Cloud\github.com\Sample\cs\struct\struct\src\struct\struct\MainClass.cs(21,13): エラー CS0019: 演算子 '==' を 'TestStruct' と '<null>' 型のオペランドに適用することはできません。 コンパイルの完了 -- エラー 1、警告 0 ========== ビルド: 0 正常終了または最新の状態、1 失敗、0 スキップ ==========
と出た・・・。testStructは値型なのでnullとは比較できないようだ・・・。
testStruct関係はいったんコメントアウトして、
// オブジェクトの宣言 TestClass testClass; // TestClassオブジェクトtestClassの宣言 TestStruct testStruct; // TestStructオブジェクトtestStructの宣言 // nullかどうかを調べる. if (testClass == null) // testClassがnullなら. {
としてみたら、
C:\Project\Cloud\github.com\Sample\cs\struct\struct\src\struct\struct\MainClass.cs(17,13): エラー CS0165: 未割り当てのローカル変数 'testClass' が使用されました。
未割当だとnull比較さえ、ダメみたいだ・・・。
(C++だとこういうのはせいぜい警告で済むのだが厳しい・・・。)
// オブジェクトの宣言 TestClass testClass; // TestClassオブジェクトtestClassの宣言 TestStruct testStruct; // TestStructオブジェクトtestStructの宣言 // testClassのインスタンスを生成. testClass = new TestClass(); // testClassは参照型であり, 初期状態はnullなので, newでインスタンス生成しないと, メ
おとなしくnewでインスタンス生成・・・。
// インスタンスの生成 testClass = new TestClass(); // TestClassのインスタンスをtestClassに格納.(実際には参照している.) // 値の代入. testClass.I = 10; // testClass.Iに10を代入.(プロパティ経由可) testClass.Str = "A"; // testClass.Strに"A"を代入.(プロパティ経由可) testClass.Show(); // testClass.Showでiとstrを表示.(メソッドも呼べる.)
プロパティもメソッドも呼べる・・・。
さて、構造体だとどうだろうか・・・。
さっきのnull判定はしょうがないとして、
// オブジェクトの宣言 TestClass testClass; // TestClassオブジェクトtestClassの宣言 TestStruct testStruct; // TestStructオブジェクトtestStructの宣言 // インスタンスの生成 testClass = new TestClass(); // TestClassのインスタンスをtestClassに格納.(実際には参照している.) // 値型の値をプロパティを経由して入れてみる. testStruct.I = 100; // testStruct.Iに100を代入.
構造体は宣言時にメモリが割り当てられているらしいので、プロパティ経由で値を入れようとしたが、
C:\Project\Cloud\github.com\Sample\cs\struct\struct\src\struct\struct\MainClass.cs(20,9): エラー CS0165: 未割り当てのローカル変数 'testStruct' が使用されました。
割り当てられているなら「newはいらないのでは?」とおもった・・・。
そこで、TestStruct.csで、
や、
というように、privateメンバフィールドをpublicにして、
// 値を直接入れてみる. testStruct.i = 100; // testStruct.iに100を代入.
メンバフィールドに直接入れてみる・・・。
これだと、
コンパイルの完了 -- エラー 0、警告 0
通ってしまう・・・。
// 文字列に直接の場合. testStruct.str = "ABC"; // testStruct.strに"ABC"を格納.
これも通る・・・。
しかし、プロパティ経由だと通らない・・・。
そして、
testStruct.Show();
メソッドでも通らなかった・・・。
どうも、アクセスできるのはメンバフィールドだけの模様・・・。
実は構造体にもnewがある・・・。
testStruct = new TestStruct(); // testStructとnew TestStructは別物で中身がコピーされる.
コメントにもあるようにこれは、TestStructオブジェクトを生成し、その参照をtestStructに渡しているわけではなく、そのオブジェクトごとtestStructにコピーしているのである・・・。
なので、まあnew TestStructは無駄になるのだが・・・。
しかし、こうすると、
// 構造体のnewは生成インスタンスのコピー. testStruct = new TestStruct(); // testStructとnew TestStructは別物で中身がコピーされる. testStruct.I = 100; // 中身がコピーされた状況だとプロパティ経由可. testStruct.Str = "ABC"; // 中身がコピーされた状況だとプロパティ経由可. testStruct.Show(); // testStruct.Showでiとstrを出力.
これでも、
コンパイルの完了 -- エラー 0、警告 0
通る・・・。
(ふしぎー・・・。)
やはり、構造体はnewで生成してコンストラクタなどで値を初期化すべきだなあ・・・。
さて、クラスは参照型、構造体は値型、ということであれば、クラスオブジェクトは参照渡し、構造体オブジェクトはコピーになることも確認しないと・・・。
最終的にMainClass.csは、
として、ClassStructMethodにtestClassオブジェクトと、testStructオブジェクトを渡して、内部でメンバを変更したらそれが反映されるかを試してみる・・・。
i = 10, str = A i = 100, str = ABC i = 20, str = X i = 100, str = ABC 続行するには何かキーを押してください . . .
最終的な結果はこうなる・・・。
testClassは参照型なので、メンバの値の変更が反映されてる・・・。
一方、testStructはコピーで渡されるので、メンバの値の変更は元のオブジェクトに影響しない・・・。
※.訂正
プロパティとメソッドが呼べない件は、
構造体 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
全てのフィールドの初期化までは呼べない制約で、生成とかの話ではない模様・・・。
Sample/cs/struct/struct/src/struct at master · bg1bgst333/Sample · GitHub