読者です 読者をやめる 読者になる 読者になる

構造体

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