非インターフェース部分
このページの目的
前のページで Rtemplate.cs を五つに分割しました。このうち、二~五番目は Rice のフィッタ、セッタ、ゲッタ、メソッドに対応しています。つまり、Rice クラスとしてのメンバの実装です。
メンバは Rice クラスの動作を決定します。つまり、これらの部位の目的はクラスのインターフェースを実装することです。
これに対して、一番目の目的は他の四つとは異なります。この部位はユーザから隠されたクラスの内部機構を実装します。
このページでは上図のように一番目の部位を更に五つに分割して、それぞれを独立したセクションで解説します。
目次:
クラス名(C#)
まずは、C#のソースファイルとして必要な部分を修正します。
ここでのポイントは C# クラス名です。Rice の実装では、Rice のクラス名に大文字の "R" を接頭辞として加えたものを使用しています。
Rtemplate が Rtype クラスの派生クラスとして定義されていることに注意してください。動的に組み込まれるクラスは Rtype クラスの派生クラスでなければなりません。
修正したソースファイルを以下に示します。クラス名、名前空間名を仕様に基づいて変更しています。
6: | using Rice; |
7: | using System; |
8: | using System.Collections.Generic; |
9: | |
10: | namespace fake { |
11: | public class Rfake : Rtype { |
クラス名には他のクラス名と衝突しないユニークな名前を選択してください。
名前空間名が重要になることは無いので、適当な名前を設定してください。ここでは、名前空間名を "fake" に変更しています。
ファイル名が "Rtemplate.cs" のままです。混乱を避ける意味でもファイル名を "Rfake.cs" に変更した方が良いでしょう。
C# の用語、機能などについては説明を割愛します。
クラス名(Rice)
テンプレートの十四行目で Rice としてのクラス名を定義しています。
public const string TYPENAME = "template";
Rice としてのクラス名は多くの場所から参照されます。このような場合は、その値の定義を一か所にまとめる必要があります。
そこで、テンプレートではクラス名を public な文字列定数として定義しています。
十六行目でデフォルトコンストラクタを定義しています。
テンプレートの定義では、フィールド _typename にクラス名を代入しています。この _typename は Rtype クラスの定義で宣言されている protected なフィールドです。
後で説明する TypeName ゲッタが、この _typename を参照してクラス名を返します。ですから、動的組込クラスのインスタンスが生成されるときは _typename が正しく設定されなければいけません。
意図しない場面でインスタンスが生成されるかもしれません。そのような場合でも正しいクラス名が生成されるようにデフォルトコンストラクタを定義しておきます。
fake クラスのためにソースファイルを修正します。文字列定数を "fake" に変更して、内部データを保持するためのフィールドを追加しています。
文字列引数を取るコンストラクタを追加しています。このコンストラクタは後の実装で使用されます。
12: | public const string TYPENAME = "fake"; // Rice としてのクラス名。 |
13: | private string InternalData = null; // 内部データ。 |
14: | |
15: | public Rfake() { _typename = TYPENAME; } // デフォルトコンストラクタ。 |
16: | |
17: | public Rfake(string val) { // 内部データを初期化するコンストラクタ。 |
18: | _typename = TYPENAME; |
19: | InternalData = val; |
20: | } |
インスタンス生成
テンプレートの十八行目で static な InstanceGenerator() メソッドを定義しています。これはクラスインスタンスの生成を司ります。このメソッドがRiceに登録されて、fake クラスが必要な場面で呼び出されます。
例えば、
fake fakeInstance;
このような宣言文があった時、fakeInstance が参照する fake クラスのインスタンスが InstanceGenerator() メソッドの呼び出しで生成されます。
Rice へのクラス登録はクラス名と引数なしで Rtype 派生クラスのインスタンスを返すデリゲートを登録することで行います。例えば、fake クラスの Rice への登録は以下のようになるでしょう。
引数なしで Rtype 派生クラスのインスタンスを返せばどのようなメソッドでもクラス登録で使用できます。
テンプレートで示したようにデフォルトコンストラクタが返すインスタンスを返せば十分のはずですが、必要ならば生成した Rtype 派生インスタンスに初期化処理を行うこともできます。
fake クラスのためにテンプレートを修正します。
22: | public static Rtype InstanceGenerator() { return new Rfake(); } |
レプリカ演算
テンプレートの十九行目で _replica() メソッドを定義しています。これは Rtype クラスの定義で宣言されている抽象メソッドで、Rtype 派生クラスで実装しなければいけません。
_replica() メソッドについて説明するために、以下の Rice ユーザ定義クラスを定義します。
fake クラスの fakeField フィールドが宣言されていることに注意してください。
1: | class userdef |
2: | fake fakeField; |
3: | // 以下に fakeField 以外のメンバ定義が続くとする。 |
4: | ... |
5: | endclass |
userdef クラスの変数が宣言されたとします。
userdef ud;
動的組み込みクラスなら登録済みのインスタンス生成メソッドが呼び出されてインスタンスが生成されます。
これに対して userdef クラスは Rice スクリプトが実行を始めてから定義されるので、インスタンス生成メソッドを登録することはできません。
Rice はユーザ定義クラスのインスタンスを次のように生成します。
Rice はユーザ定義クラスの定義からオリジナルのインスタンスを一つ生成して、クラス名とオリジナルを登録します。そして、ユーザ定義クラスのインスタンス生成要求に対してはオリジナルのコピーを返します。
このユーザ定義クラスインスタンスを生成するコピー演算は、通常のコピー演算と区別するためにレプリカ演算と呼ばれます。
さて、userdef クラスの定義には fake クラスの fakeField フィールドが有ります。つまり、userdef クラスのオリジナルも fakeField を持っています。userdef の生成はオリジナルのレプリカですから fakeField もレプリカされます。この fakeField のレプリカで呼び出されるのが _replica() メソッドです。
つまり、_replica() メソッドは組み込みクラスがユーザ定義クラスのフィールドになった場合に、インスタンス生成の過程で呼び出されるコピーのためのメソッドです。
インスタンスのレプリカで特別な処理が必要になるかもしれません、その時は _replica() メソッド内でそれを行うことができます。
fake クラスのためにテンプレートを修正します。
23: | public override Rtype _replica(VirtualMachine vm) { return new Rfake(); } |
メソッドの引数については無視してください。これは以前の実装の痕跡です。
Fitted メソッド
テンプレートの二十行目で Fitted() メソッドを定義しています。これは Rtype クラスの定義で宣言されている抽象メソッドで、Rtype 派生クラスで実装しなければいけません。
後で説明する Fitted ゲッタがこの Fitted() メソッドを参照してインスタンスの初期化状況を返します。
fake クラスの初期化済み/未済を次のように定義します。
InternalData フィールドが非 null の場合は初期化されています。
fake クラスのためにテンプレートを修正します。
24: | public override Rbool Fitted(VirtualMachine vm) { return new Rbool(InternalData != null); } |
メソッドの引数については無視してください。これは以前の実装の痕跡です。
fake クラスは内部データを持つので初期化状態に意味があります。しかしながら、初期化状態に意味の無いクラスも有ります。
そのような場合は Fitted() メソッドが常に Rbool(true) を返すように実装してください。
修正済みコード
上記の修正済みのコード全体を以下に示します。
6: | using Rice; |
7: | using System; |
8: | using System.Collections.Generic; |
9: | |
10: | namespace fake { |
11: | public class Rfake : Rtype { |
12: | public const string TYPENAME = "fake"; // Rice としてのクラス名。 |
13: | private string InternalData = null; // 内部データ。 |
14: | |
15: | public Rfake() { _typename = TYPENAME; } // デフォルトコンストラクタ。 |
16: | |
17: | public Rfake(string val) { // 内部データを初期化するコンストラクタ。 |
18: | _typename = TYPENAME; |
19: | InternalData = val; |
20: | } |
21: | |
22: | public static Rtype InstanceGenerator() { return new Rfake(); } |
23: | public override Rtype _replica(VirtualMachine vm) { return new Rfake(); } |
24: | public override Rbool Fitted(VirtualMachine vm) { return new Rbool(InternalData != null); } |