テキスト篇:第11章「構造体」から
ゲーム開発演習:図形の描画 など
第11章 構造体
p.275 構造体とは
・C#の基であるC言語にあった「クラスの基になったデータ構造」で、複数の変数や配列をグループ化して名前を付け、
ユーザー定義の型であるとして用いる手法
・対して、C#はこれをっ拡大定義して、メソッドやプロパティなども持つことができるようにしている
・結果的に、C#の構造体は「軽量クラス」の位置づけで、クラスの機能を削って軽くしたもの。
※そのため、Unityなどで活用されており、Vector3構造体など、Unity独自の構造体の出番が多い
・クラスの違いは:
①値型(p.188)なので、メソッドに渡した場合、全データメンバのコピーが発生する
②継承(p.223)できない(インターフェイスの実装であれば可能)
③引数のないコンストラクタ(p.167)をプログラマが定義することはできない(自動的に用意されるもののみ)
④インスタンス変数(p.154)の初期化は不可
⑤デストラクタ(p.171)は定義不可
・定義書式: struct 構造体名 {…}
例: struct Map { int x; int y; } //X座標とY座標を持つマップの構造体
・インターフェイス(p.255)を実装する場合の書式: struct 構造体名 : インターフェイス名 {…}
・クラスと同様に構造体オブジェクトを宣言して利用する(宣言のみでオブジェクトが生成される)
・宣言書式: 構造体名 変数名; //構造体を型とする変数の宣言
・メンバの利用書式はクラスのインスタンスメンバと同様で「変数名.メンバ名」で良い
例: Map hoimin; hoimin.x = 50; hoimin.y = 60; //マップ「hoimin」を生成しX座標Y座標を設定
・構造体のメンバとしては、データメンバに加えて、メソッド、プロパティ、インデクサ、コンストラクタ(引数有り)などが
定義できる
アレンジ演習:p.276 struct01.cs
・p.277にあるとおり、インスタンス変数xを初期化するとどういうエラーになるか確認しよう ⇒ 初期化において変数に「10.0以上の言語バージョンをお使いください」と表示される ※.NETフレームワークでは6.xに該当するが、VS2022に標準インストールされるのは4.7.2 https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-versioning ※よって「Visual Studio用 .NET SDK」のページで上位バージョンの.NETフレームワークをインストールすれば解決するが、 他の影響に配慮が必要 https://dotnet.microsoft.com/ja-jp/download/visual-studio-sdks?cid=getdotnetsdk
作成例
//アレンジ演習:p.276 struct01.cs
using System;
struct MyStruct //構造体MyStructの定義
{
public int x; //公開のインスタンス変数
//public int x = 10; //公開のインスタンス変数 ※初期化するとエラーになる
public void show() //公開のインスタンスメソッド
{
Console.WriteLine("x = {0}", x);
}
}
class struct01
{
public static void Main()
{
MyStruct ms; //構造体MyStruct型の変数msの定義(構造体オブジェクト生成)
ms.x = 10; //変数ms経由でインスタンス変数xに代入
ms.show(); //変数ms経由でインスタンスメソッドshow()を実行
}
}
p.277 静的メンバを持つ構造体
・クラスと同様に、構造体にも静的メンバ(p.202)を定義できる ・よって、構造体に所属するデータメンバは静的データメンバにし、これを用いるメソッド、プロパティは静的メンバにすると良い ・「構造体名.静的メンバ名」でアクセスできる
アレンジ演習:p.277 struct02.cs その1
・静的データメンバxを扱う静的プロパティPXを追加定義 ・xをprivateに変更し、Main()では静的プロパティPX経由でアクセスするように変更
作成例
//アレンジ演習:p.277 struct02.cs
using System;
struct MyStruct //構造体の定義
{
static int x = 10; //【変更】非公開の静的データメンバ
static int[] myarray = new int[10]; //静的データメンバである配列
public static void show() //公開の静的メソッド
{
Console.WriteLine("x = {0}", x); //静的データメンバを利用
}
public static int PX //【追加】公開の静的プロパティ
{
set { x = value; }
get { return x; }
}
}
class struct02
{
public static void Main()
{
MyStruct.show(); //公開の静的メソッドなので「構造体名.メソッド名」で呼べる
MyStruct.PX = 20; //【変更】公開の静的プロパティなので「構造体名.プロパティ名」で利用可
MyStruct.show(); //20になっている
}
}
アレンジ演習:p.277 struct02.cs その2
・MyStruct構造体には静的データメンバである配列myarrayが定義されているが使われてない ・これを外部から利用できるようにインデクサ(p.212)を追記し、Main()から利用してみよう
作成例
//アレンジ演習:p.277 struct02.cs その2
using System;
struct MyStruct //構造体の定義
{
static int x = 10; //【変更】非公開の静的データメンバ
static int[] myarray = new int[10]; //静的データメンバである配列
public static void show() //公開の静的メソッド
{
Console.WriteLine("x = {0}", x); //静的データメンバを利用
}
public static int PX //【追加】公開の静的プロパティ
{
get { return x; }
set { x = value; }
}
public int this[int i] //【追加】公開のインデクサ(静的にはできない)
{
get { return myarray[i]; } //静的データメンバである配列は利用可
set { myarray[i] = value; }
}
}
class struct02
{
public static void Main()
{
MyStruct.show(); //公開の静的メソッドなので「構造体名.メソッド名」で呼べる
MyStruct.PX = 20; //【変更】公開の静的プロパティなので「構造体名.プロパティ名」で利用可
MyStruct.show(); //20になっている
MyStruct m; m[0] = 30; //【追加】変数を定義し、変数経由でインデクサ(set)を呼ぶ
Console.WriteLine("m[0] = {0}", m[0]); //【追加】変数経由でインデクサ(get)を呼ぶ
}
}
アレンジ演習:p.277 struct02.cs その3
・インデクサを静的にはできないので、Main()で変数を定義し、変数経由でインデクサを呼んでいる ・しかし、インデクサで用いている静的データメンバである配列は構造体に所属するので、オブジェクトには含まれていない ・このことを、変数を追加定義し、その変数経由でインデクサを呼ぶことで確認しよう
作成例
//アレンジ演習:p.277 struct02.cs その3
using System;
struct MyStruct //構造体の定義
{
static int x = 10; //【変更】非公開の静的データメンバ
static int[] myarray = new int[10]; //静的データメンバである配列
public static void show() //公開の静的メソッド
{
Console.WriteLine("x = {0}", x); //静的データメンバを利用
}
public static int PX //【追加】公開の静的プロパティ
{
get { return x; }
set { x = value; }
}
public int this[int i] //【追加】公開のインデクサ(静的にはできない)
{
get { return myarray[i]; } //静的データメンバである配列は利用可
set { myarray[i] = value; }
}
}
class struct02
{
public static void Main()
{
MyStruct.show(); //公開の静的メソッドなので「構造体名.メソッド名」で呼べる
MyStruct.PX = 20; //【変更】公開の静的プロパティなので「構造体名.プロパティ名」で利用可
MyStruct.show(); //20になっている
MyStruct m; m[0] = 30; //【追加】変数を定義し、変数経由でインデクサ(set)を呼ぶ
Console.WriteLine("m[0] = {0}", m[0]); //【追加】変数経由でインデクサ(get)を呼ぶ
MyStruct n; //【追加】変数を追加定義する(m[0]とn[0]は同じ実体になる)
Console.WriteLine("n[0] = {0}", n[0]); //【追加】この変数経由でインデクサを呼ぶと同じ値になる
}
}
p.278 コンストラクタを持つ構造体
・引数のあるコンストラクタを構造体に定義できる ・このコンストラクタは、構造体オブジェクトの生成時に、new演算子を用いて、 構造体型の変数 = new 構造体名(実引数,…); などとすることで呼び出せる。 ・なお、引数のないコンストラクタは定義できないが、自動的に全データメンバを初期化するデフォルトコンストラクタが用意されるので 「new 構造体名()」で明示的に呼び出すことも可能
p.278(構造体型の変数の間の代入)
・構造体型の2変数において代入が可能で、全データメンバの値がコピーされる ・よって、代入後の変数経由で値を書き換えた場合、コピー後の値なので、コピー元には反映しない ※同様のことをクラスで行うと参照により、代入元にも反映する
アレンジ演習:p.278 struct03.cs
・引数のないコンストラクタを追記するとどういうエラーになるか確認しよう ⇒初期化において変数に「10.0以上の言語バージョンをお使いください」と表示される ※よって「Visual Studio用 .NET SDK」のページで上位バージョンの.NETフレームワークをインストールすれば解決するが、 他の影響に配慮が必要
作成例
//アレンジ演習:p.278 struct03.cs
using System;
struct MyStruct
{
public int x; //公開のデータメンバ
public MyStruct(int a) //引数のあるコンストラクタ
{
x = a;
}
//public MyStruct() { }//引数のないコンストラクタは記述不可
}
class struct03
{
public static void Main()
{
MyStruct ms = new MyStruct(); //用意された引数のないコンストラクタが呼ばれる
Console.WriteLine("ms.x = {0}", ms.x); //自動的に0で初期化されている
MyStruct ms2 = new MyStruct(100); //引数のあるコンストラクタが呼ばれる
Console.WriteLine("ms2.x = {0}", ms2.x); //引数経由で100が代入されている
MyStruct ms3; //用意された引数のないコンストラクタが呼ばれる
ms3 = ms2; //ms2の値をms3に代入すると構造体オブジェクトのコピーになる
Console.WriteLine("ms3.x = {0}", ms3.x); //値(100)がコピーされている
ms3.x = 50; //コピー先の構造体オブジェクトのms3.xの値を50に変更しても
Console.WriteLine("ms2.x = {0}", ms2.x); //コピー元の構造体オブジェクトの値には影響しない
}
}