Home Notices Documents Classes Download Others Rice
Documents  >  開発  >  動的組み込みクラスの実装  >  非インターフェース部分
非インターフェース部分の実装

このページの目的

このページではRtemplate.csを五つに分けた最初の部分を詳説します。


この最初の部分は、他の四つの部分とは異なります。

他の四つの部分は、フィッタやメソッドなどのクラスのメンバやその振舞いを指定します。つまり、ユーザから見えるクラスのインターフェースを実装することが目的です。

これに対して、最初の部分はクラス名の決定や生成時の振舞いなどのユーザから隠されたクラスの内部機構、すなわち、インターフェースから利用される機能を実装することが目的です。



このページでは図で示したように、最初の部分を更に五つの部分に分けてそれぞれを独立したセクションで解説します。

①クラス名等(C#)

この部分はRiceとして必要な部分というよりは、C#のソースファイルとして必要な部分です。Usingディレクティブ、名前空間、クラス名やアクセス修飾子の指定です。

このページはC#の解説では無いので、これらの詳細については説明をしません。もし、わからない部分があるなら各自で検索をお願いします。


ここでのポイントはクラス名です。

ここではC#のクラス名を"Rtemplate"としています。詳しくは後述しますが、この"Rtemplate"がCookerGXに組み込みまれた場合のRice上での名前は"template"になります。

C#のクラス名(Rtemplate)とRiceのクラス名(template)が全く異なっていても問題はありませんが、わざわざ混乱の素を導入する必要はありません。

Riceでは、すべての組み込みクラスがRiceのクラス名に大文字のRを接頭辞として加えたものをC#のクラス名とすることを推奨しています。


Rtemplate がRtypeの派生クラスとして定義されることに注意してください。組み込みクラスはRtypeクラスの派生クラスでなければなりません。


②クラス名(Rice)

最初の図を見てください。十四行目でRiceとしての名前を定義しています。

public const string TYPENAME = "template";

ここでは、文字列定数として"template"を定義しています。このTYPENAME定数は必ず定義しなければいけない訳ではありません。例えば、このRtemplateのRiceクラス名が必要な全ての場面で毎回"template"と打ち込んでも構いません。

しかし、コーディングのテクニックとして、このような多数の場面で同じ値を参照する場合は、その値を定数化すべきです。


十六行目で引数なしのコンストラクタ(デフォルトコンストラクタ)を定義しています。これにより、デフォルトコンストラクタがC#コンパイラーによって自動生成されるのを防いでいます。

このデフォルトコンストラクタは、_typenameにクラス名("template")を代入しています。この_typenameはRtypeの定義で宣言されているprotectedなフィールドです。

後で出てくるTypeNameゲッタがこの_typenameを参照してクラス名を返します。ですから、組み込みクラスのインスタンスが生成されるときはインスタンス毎に_typenameが正しく設定されなければいけません。

当然ですが、自動生成されるデフォルトコンストラクタはこのような設定を行いません。意図しない場面でインスタンスが生成されるかもしれません。そのような場合でも正しいインスタンスが生成されるようにデフォルトコンストラクタを定義しておくべきです。


Rtemplateは引数なしのコンストラクタしか定義していませんが、実際の組み込みクラスは幾つかの引数有りのコンストラクタを持つでしょう。そのようなコンストラクタでも_typenameを設定することを忘れないで下さい。

③インスタンス

十八行目でstaticなInstanceGenerator()メソッドを定義しています。これはクラスインスタンスの生成を司ります。このメソッドがRiceに登録されて、templateクラスが必要な場面で呼び出されます。

例えば、次のRiceソースコードがあったとします。


template temp;


templateクラスの変数の宣言文です。この時、tempが参照するtemplateクラスのインスタンスがInstanceGenerator()メソッドの呼び出しで生成されます。

Riceへの組み込みクラス登録はクラス名と引数なしでRtypeを返すメソッドを登録することで行います。例えば、templateクラスのRiceへの登録は以下のようになるでしょう。



引数なしでRtypeを返せばどのようなメソッドでもクラス登録で使用できます。

例えば名前がInstanceGeneratorである必要はありませんし、返すべきRtypeインスタンスに初期化処理を行っても大丈夫です。

しかしながら、実際にはサンプルで示したようにデフォルトコンストラクタが返すインスタンスを返せば十分のはずです。

④レプリカ

十九行目で_replica()メソッドを定義しています。これはRtypeの定義で宣言されている抽象メソッドで、Rtypeの派生クラス、すなわち組み込みクラスで必ず実装しなければいけません。

_replica()メソッドが何のためのメソッドかを説明します。


次のユーザ定義クラスがあるとします。

1:class userdef
2:template _temp;
3:// 以下に_temp以外のメンバ定義が続く
4:...
5:endclass

ここでポイントはtemplateクラスのフィールドが宣言されていることです。


このuserdefクラスの変数がRiceスクリプトのどこかで宣言されたとしましょう。


userdef ud;


この時、何が起こるでしょうか?

例えば、templateクラスなら登録されたインスタンス生成メソッド(InstanceGenerator())が呼び出されてtemplateクラスのインスタンスが生成されます。これはtemplateクラスがRiceスクリプトの実行前に定義されて登録されているからできることです。

これに対してuserdefクラス(ユーザ定義クラス)はRiceスクリプトが実行を始めてから定義されるクラスです。つまり、どんなクラスかは実行を開始するまでわかりません。

このようなクラスにInstanceGenerator()のようなインスタンス生成メソッドの登録を要求することはできません。このようなクラスのインスタンスを生成するにはどうしたらよいでしょうか?


Riceはこの問題に次のように対処します。

ユーザ定義クラスの定義を読み込んだRiceは、その定義からオリジナルのインスタンスを一つ生成します。そして、クラス名とそのインスタンスを登録します。つまり、組み込みクラスがインスタンス生成メソッドを登録するのに対して、ユーザ定義クラスはオリジナルインスタンスを登録するわけです。

Riceはユーザ定義クラスの生成要求に対してオリジナルインスタンスのコピーを返すことで対処します。このユーザ定義クラスの生成要求に対するオリジナルインスタンスのコピーのことをRiceではレプリカ演算と呼びます。


さて、userdefクラスの定義を思い出してください。userdefはtemplateクラスの_tempフィールドを持っています、つまり、userdefの登録されたオリジナルも_tempを持っています。userdefの生成はオリジナルのコピーですから_tempもコピーされなければなりませんし実際にコピーされます。この_tempのコピー時に呼び出されるのが_replica()メソッドです。

つまり、_replica()メソッドとは組み込みクラスがユーザ定義クラスのフィールドになった場合に、そのユーザ定義クラスのインスタンス生成の過程で呼び出される、コピー演算のためのメソッドです。

コピー時に特別な処理が必要になるかもしれません、その時は_replica()メソッド内でそれを行うことができます。

特別な処理を行うことも出来ますが実際にはサンプルで示したようにデフォルトコンストラクタが返すインスタンスを返せば十分のはずです。


_replica()メソッドの引数については無視してください。これは以前の実装の痕跡であり使用する必要はないはずです。

⑤Ftted getter

二十行目でFitted()メソッドを定義しています。これはRtypeの定義で宣言されている抽象メソッドで、Rtypeの派生クラス、すなわち組み込みクラスで必ず実装しなければいけません。

後で出てくるFitted ゲッタがこのFitted()を参照してインスタンスの初期化状況を返します。

templateクラスはインスタンスの状態を保持するフィールドがないので、初期化済み/未済という考え方は必要ありません。このような場合はFitted()メソッドが常にtrue(return new Rbool(true);)を返すようにすれば十分です。


初期化済み/未済に意味のあるクラスの場合をregexクラスを例にして解説します。

1:public class Rregex : Rtype {
2:private Regex _pattern = null;
3:public const string TYPENAME = "regex";
4:
5:public override Rbool Fitted(VirtualMachine vm) { return new Rbool(_pattern != null); }
6:...
7:
※説明に必要な部分だけを表示していることに注意してください。

regex(Rregex)クラスは正規表現による文字列の一致判定を行うためのクラスであり、そのためにフィールド_patternとしてC#の正規表現クラス持ちます。

このクラスが正常に動作するためには_patternに正規表現パターンで初期化されたRegexクラスのインスタンスが割り当てられている必要があります。

例えば、

_pattern = new Regex("^Rice"); // 先頭がRiceで始まることを表す正規表現

の様に割り当てる必要があるわけです。

実際にregex(Rregex)クラスはフィッタでこの割り当てを行います。そして_patternを使って一致判定が行われるわけです。

_patternに割り当てがされていれば一致判定できる状態ですから、これは初期化済みといえます。

_patternに割り当てがされていない(null)ならば一致判定できませんから、初期化未済です。

このような、インスタンスの状態を表すためにFitted()メソッドが使用されます。上記であれば次のようになっています。


return new Rbool(_pattern != null);


_patternのnullチェックの結果を返しています。これがFittedゲッタによって参照されてインスタンスの初期化状態を表すわけです。

初期化状態の判定が複雑な場合もあるかもしれませんが、ほとんどの場合はnullチェックの様な単純な判定で初期化状態を判定できるはずです。


Fitted()メソッドの引数については無視してください。これは以前の実装の痕跡であり使用する必要はないはずです。

次のページでは

これでクラスのインターフェースから利用される諸機能についての説明は終了です。ここで説明した機能は最低限のものですが、これらが正しく実装されていれば組み込みクラスも正しく動作します。

次のページではクラスのインターフェースであるフィッタの実装について説明します。

Next
Previous
Copyright © CookerGX All rights reserved.