テキスト篇:p.282「構造体でインターフェイスを実装する」
ゲーム開発演習:図形の塗りつぶし、文字列の描画 など
p.282 構造体でインターフェイスを実装する
・C#の構造体は「軽量クラス」の位置づけで、クラスの機能を削って軽くしたもの。 ※そのため、Unityなどで活用されており、Vector3構造体など、Unity独自の構造体の出番が多い ・継承はできないが、インターフェイスの実装であれば可能 ・クラスにおけるインターフェイスの実装とほぼ同じだが、インターフェイスを実装した構造体の利用には new演算子による明示的なオブジェクトの生成が必要
アレンジ演習:p.282 struct05.cs
・「= new MyStruct();」を省略するとどういうエラーになる確認しよう ・適当なインターフェイスの定義を追加し、構造体が複数のインターフェイスを実装可能か確認しよう
作成例(p.263「複数のインターフェイスを実装する」参照:抽象メソッド名が異なる場合)
//アレンジ演習:p.282 struct05.cs
using System;
interface IMyInterface { //インターフェイス①の定義
void Show(); //抽象メソッド
int xprop { //抽象プロパティ
get;
set;
}
}
interface Flyable { //【追加】インターフェイス②の定義
void Fly(); //抽象メソッド(インターフェイス①とは名前が異なる)
}
struct MyStruct : IMyInterface, Flyable { //【変更】2インターフェイスを実装する構造体
int x;
public void Show() { //インターフェイス①の抽象メソッドのオーバーライド
Console.WriteLine("x = {0}", x);
}
public void Fly() { //【以下追加】インターフェイス②の抽象メソッドのオーバーライド
Console.WriteLine("Flyable.x = {0}", x);
}
public int xprop { //抽象プロパティのオーバーライド
get { return x; }
set { x = value; }
}
}
class struct05 {
public static void Main() {
MyStruct msNew = new MyStruct(); //newによる明示的なオブジェクト生成が必要
msNew.Show(); //インターフェイス①の抽象メソッドのオーバーライドを呼ぶ
msNew.xprop = 20; //抽象プロパティのオーバーライド(set)を呼ぶ
msNew.Fly(); //【変更】インターフェイス②の抽象メソッドのオーバーライドを呼ぶ
}
}
作成例(p.263「複数のインターフェイスを実装する」参照:抽象メソッド名が同じ場合)
//アレンジ演習:p.282 struct05.cs
using System;
interface IMyInterface { //インターフェイス①の定義
void Show(); //抽象メソッド
int xprop { //抽象プロパティ
get;
set;
}
}
interface Flyable { //【追加】インターフェイス②の定義
void Show(); //抽象メソッド
}
struct MyStruct : IMyInterface, Flyable { //【変更】2インターフェイスを実装する構造体
int x;
void IMyInterface.Show() { //【変更】インターフェイス①の抽象メソッドのオーバーライド
Console.WriteLine("x = {0}", x);
}
void Flyable.Show() { //【以下追加】インターフェイス②の抽象メソッドのオーバーライド
Console.WriteLine("Flyable.x = {0}", x);
}
public int xprop { //抽象プロパティのオーバーライド
get { return x; }
set { x = value; }
}
}
class struct05 {
public static void Main() {
MyStruct msNew = new MyStruct(); //newによる明示的なオブジェクト生成が必要
IMyInterface i1; //【追加】インターフェイス①の参照変数
i1 = msNew; //【追加】構造体オブジェクトへの参照を代入
i1.Show(); //【変更】インターフェイス①の参照経由で抽象メソッドのオーバーライドを呼ぶ
msNew.xprop = 20; //抽象プロパティのオーバーライド(set)を呼ぶ
Flyable i2; //【追加】インターフェイス②の参照変数
i2 = msNew; //【追加】構造体オブジェクトへの参照を代入
i2.Show(); //【追加】インターフェイス②の参照経由で抽象メソッドのオーバーライドを呼ぶ
}
}
p.284 構造体をメソッドに渡す
・クラスオブジェクトをメソッドに渡すと参照渡しになり、引数で受け取ったオブジェクトのデータメンバの値をメソッド側で操作すると 呼び出し元のオブジェクトに反映する ・構造体オブジェクトをメソッドに渡すと値渡しになりコピーされて渡されるので、引数で受け取ったオブジェクトのデータメンバの値を メソッド側で操作しても呼び出し元のオブジェクトには反映しない ・よって、構造体オブジェクトをメソッドに渡し、クラスオブジェクトと同様に扱いたい場合は、ref(p.188)を指定して 参照渡しにすると良い
アレンジ演習:p.284 struct06.cs
・MyData構造体と、MyDataClassクラスの双方に、データメンバx、yの値を表示するShowメソッドを追加してコードをシンプルにしよう
作成例
//アレンジ演習:p.284 struct06.cs
using System;
struct MyData { //構造体の定義
public int x, y;
public void show(string s) { //【以下追加】
Console.WriteLine("{0}\nx = {0}, y = {1}", s, x, y);
}
}
class MyDataClass { //(比較用の)クラスの定義
public int x, y;
public void show(string s) { //【以下追加】
Console.WriteLine("{0}\nx = {0}, y = {1}", s, x, y);
}
}
class ChangeData { //構造体とクラスのオブジェクトを操作するメソッドを持つクラス
public void change(ref MyData md) { //構造体オブジェクトを参照渡しする
int temp; //交換用の変数を用いてxとyの値を交換(呼び出し元に反映する)
temp = md.x;
md.x = md.y;
md.y = temp;
}
public void change(MyData md) { //構造体オブジェクトを値渡しする
int temp; //交換用の変数を用いてxとyの値を交換(呼び出し元には反映しない)
temp = md.x;
md.x = md.y;
md.y = temp;
}
public void change(MyDataClass m) { //クラスオブジェクトを参照渡しする
int temp; //交換用の変数を用いてxとyの値を交換(呼び出し元に反映する)
temp = m.x;
m.x = m.y;
m.y = temp;
}
}
class struct06 {
public static void Main() {
MyData md = new MyData(); //newで明示的に構造体オブジェクトを生成
md.x = 10;
md.y = 20;
md.show("最初の状態"); //【変更】
ChangeData cd = new ChangeData();
cd.change(md); //構造体オブジェクトを値渡しする
md.show("cd.change(md)後"); //【変更】変化しない
cd.change(ref md); //構造体オブジェクトを参照渡しする
md.show("cd.change(ref md)後");//【変更】変化する
MyData m; //newを用いずに構造体オブジェクトを生成
m.x = 10;
m.y = 20;
m.show("最初の状態"); //【変更】
cd.change(ref m); //構造体オブジェクトを参照渡しする
m.show("cd.change(ref m)後"); //【変更】変化する
MyDataClass mdc = new MyDataClass(); //クラスオブジェクトを生成
mdc.x = 10;
mdc.y = 20;
mdc.show("最初の状態"); //【変更】
cd.change(mdc); //クラスオブジェクトを参照渡しする
mdc.show("cd.change(mdc)後"); //【変更】変化する
}
}
p.288 DateTime構造体を使ってみる
・C#が提供する構造体は多数あり、最も利用されているのは.NET基本型の構造体(p.45参照) ・例えば、int型は内部的にはSystem.Int32構造体として定義されている https://learn.microsoft.com/ja-jp/dotnet/fundamentals/runtime-libraries/system-int32 ・p.45で説明したMaxValue、MinValueは、この構造体の持つ定数データメンバになっている。 ・p.45で説明したParse(string)は、この構造体の持つ静的メソッド。 ・p.47で説明したConvert.ToInt32(string)は、Convertクラスのメソッドだが、この構造体が IConvertible インターフェイスを実装しているため、Parseと同様に利用できる ・また、日付時刻の利用に便利なSystem.DateTime構造体も提供されている
p.288 DateTime構造体を使ってみる:主なメンバ
・Nowプロパティ:getのみの静的プロパティでDateTime型。現在日付時刻を持つDateTime構造体オブジェクトを返す
・Dateプロパティ:getのみのプロパティでDateTime型。時刻を00:00:00にしたDateTime構造体オブジェクトを返す
※ このままConsole.Writeなどで表示すると「yyyy/mm/dd 0:00:00」形式となる
・Yearプロパティ:getのみのプロパティでint型。西暦年を返す。
・Monthプロパティ:getのみのプロパティでint型。月を1から12で返す。(他言語とは異なり1月から)
・Dayプロパティ:getのみのプロパティでint型。日を返す。
・DayOfWeekプロパティ:getのみのプロパティでDayOfWeek列挙型。曜日を示す列挙子を返す。
※ このままConsole.Writeなどで表示すると英語の曜日名("Sunday"などのフルスペル)となる
・DayOfYearプロパティ:getのみのプロパティでint型。その年の何日目かを返す。
・Hourプロパティ:getのみのプロパティでint型。24時間での時を0から23で返す。
・Minuteプロパティ:getのみのプロパティでint型。分を0から59で返す。
・Secondプロパティ:getのみのプロパティでint型。秒を0から59で返す。
・MilliSecondプロパティ:getのみのプロパティでint型。ミリ秒を0から999で返す。
・DateTime(int y, int m, int d)コンストラクタ:西暦y年m月d日の構造体オブジェクトを生成
・DateTime(int y, int m, int d, int h, int n, int s)コンストラクタ:西暦y年m月d日h時n分s秒の構造体オブジェクトを生成
・ToShortDateStringメソッド:インスタンスメソッドでstring型。「yyyy/mm/dd」形式の文字列を返す
アレンジ演習:p.290 DateTime01.cs
・コンソールから入力した年、月、日、時、分、秒でDateTime構造体オブジェクトを生成し、各プロパティやメソッドの動作を確認しよう ・また、不正な年、月、日、時、分、秒を入力するとどうなるか確認しよう ⇒ArgumentOutOfRangeExceptionとなり実行時エラーで異常終了する(年は1以上ならOK) ・曜日を日本語曜日1文字で表示するようにしよう ⇒DayOfWeek列挙型をintにキャストすると0~6の整数になるので、これを添字にして用いると良い
作成例
//アレンジ演習:p.290 datetime01.cs
using System;
class datetime01
{
public static void Main()
{
char[] dow = {'日','月','火','水','木','金','土'}; //【追加】
DateTime dt = DateTime.Now;
Console.WriteLine("今日は{0}年{1}月{2}日({3})です",
dt.Year, dt.Month, dt.Day, dow[(int)dt.DayOfWeek]); //【変更】年月日曜日をプロパティで得る
Console.WriteLine("現在{0}時{1}分{2}秒{3}ミリセコンドです",
dt.Hour, dt.Minute, dt.Second, dt.Millisecond); //時分秒ミリ秒をプロパティで得る
Console.WriteLine("dt.Date = {0}", dt.Date); //「yyyy/mm/dd 0:00:00」を表示
Console.WriteLine("短い日付形式 = {0}", dt.ToShortDateString()); //「yyyy/mm/dd」を表示
//【以下追加】
int y, m, d, h, n, s; //年月日時分秒
Console.Write("年:"); y = int.Parse(Console.ReadLine());
Console.Write("月:"); m = int.Parse(Console.ReadLine());
Console.Write("日:"); d = int.Parse(Console.ReadLine());
Console.Write("時:"); h = int.Parse(Console.ReadLine());
Console.Write("分:"); n = int.Parse(Console.ReadLine());
Console.Write("秒:"); s = int.Parse(Console.ReadLine());
dt = new DateTime(y, m, d, h, n, s); //入力した年月日時分秒で構造体オブジェクトを生成
Console.WriteLine("{0}年{1}月{2}日({3})です",
dt.Year, dt.Month, dt.Day, dow[(int)dt.DayOfWeek]); //年月日曜日をプロパティで得る
Console.WriteLine("{0}時{1}分{2}秒{3}ミリセコンドです",
dt.Hour, dt.Minute, dt.Second, dt.Millisecond); //時分秒ミリ秒をプロパティで得る
Console.WriteLine("dt.Date = {0}", dt.Date); //「yyyy/mm/dd 0:00:00」を表示
Console.WriteLine("短い日付形式 = {0}", dt.ToShortDateString()); //「yyyy/mm/dd」を表示
}
}