Non-interface part
Purpose of this page
We split Rtemplate.cs into five parts on the previous page. The 2nd to 5th correspond to Rice fitters, setters, getters, and methods. That is, the implementation of the member as the Rice class.
The member determines the behavior of the Rice class. So the purpose of these parts is to implement class interfaces.
On the other hand, the purpose of the first part is different from the other four. This part implements the internal mechanism of the class hidden from the user.
As shown in the above figure, the first part is further divided into five, and each is explained in an independent section.
Table of contents:
Class name - C# -
First, we modify the part required as a C # source file.
The point here is the C# class name. The Rice implementation uses the Rice class name with an "R" as prefix.
Please notice that Rtemplate is defined as a derived class of the Rtype class. The dynamic embedded class must be a derived class of the Rtype class.
The modified source file is shown below. The class name and namespace name are changed based on the specifications.
6: | using Rice; |
7: | using System; |
8: | using System.Collections.Generic; |
9: | |
10: | namespace fake { |
11: | public class Rfake : Rtype { |
Please choose a unique class name that does not conflict with other class names.
The namespace name is not important, so set a name you like. Here, the namespace name is changed to "fake".
The file name is still "Rtemplate.cs". It's a good idea to rename the file to "Rfake.cs" to avoid confusion.
We will omit the explanation of C # terms and functions.
Class name - Rice -
A class name on the Rice is defined at the 14th line of the template.
public const string TYPENAME = "template";
A class name as the Rice will be referenced from many places. In such a case, it is necessary to put the definition of the value in one place.
Therefore, the template defines the class name as a public string constant.
The default constructor is defined at the 16th line.
The class name is assigned to the _typename field. This _typename is a protected field declared in the Rtype class definition.
The TypeName getter, described later, references the _typename and returns the class name. So the _typename must be set correctly when an instance of a dynamic embedded class is created.
Instances may be created in unintended situations. We should define the default constructor so that the correct class name will be generated even in such a case.
The template is modified for the fake class. We've changed the string constant to "fake" and added a field to hold an internal data.
We add a constructor that takes a string argument. This constructor will be used in later implementations.
12: | public const string TYPENAME = "fake"; // Class name as Rice class. |
13: | private string InternalData = null; // Internal data. |
14: | |
15: | public Rfake() { _typename = TYPENAME; } // Default constructor. |
16: | |
17: | public Rfake(string val) { // A constructor that initializes the internal data. |
18: | _typename = TYPENAME; |
19: | InternalData = val; |
20: | } |
Instance generation
The static InstanceGenerator() method is defined at the 18th line of the template. This method has responsibility for creating class instance. This method will be registered in Rice and called when the fake class is needed.
For example:
fake fakeInstance;
When there is a declaration statement like above, an instance of fake class is generated by calling the InstanceGenerator () method.
Class registration in Rice is done by registering a class name and a delegate that returns an instance of the Rtype-derived class with no arguments. For example, registering the fake class with Rice would look like this:
Any method that returns an instance of an Rtype-derived class with no arguments can be used in class registration.
It should be sufficient to return an instance returned by the default constructor as shown in the template, but you can also initialize the generated Rtype-derived instance if you wish.
We modify the template for the sake of the fake class.
22: | public static Rtype InstanceGenerator() { return new Rfake(); } |
Replica operation
The _replica() method is defined at the 19th line of the template. This is an abstract method declared in the Rtype class definition and must be implemented in an Rtype-derived class.
First, we define the following Rice user-defined class for explaining the _replica() method.
Please pay attention that the fakeField field of the fake class is declared.
1: | class userdef |
2: | fake fakeField; |
3: | // Member definitions other than fakeField. |
4: | ... |
5: | endclass |
Suppose a variable of the userdef class is declared.
userdef ud;
If it is dynamic embedded class, the registered instance creation method will be called to create an instance.
On the other hand, the userdef class is defined after the Rice script starts executing, so you cannot register an instantiation method.
Rice instantiates a user-defined class as follows:
Rice creates one original instance from the definition of the user-defined class and registers the class name and the original. Then, it responds to the instantiation request of the user-defined class by returning a copy of the original.
The copy operation that creates an user-defined class instance is called a replica operation to distinguish it from a normal copy operation.
The userdef class has the fakeField field of the fake class. That is, the original of the userdef class also has a fakeField. Since the userdef generation is a replica of the original, fakeField is also replicated. The _replica() method is called on this replica of the fakeField.
In other words, the _replica() method is a method for copy operation that is called during the instantiation process when a dynamic embedded class becomes a field of the user-defined class.
If your instance replica requires special processing, you can do so within the _replica() method.
We modify the template for the sake of the fake class.
23: | public override Rtype _replica(VirtualMachine vm) { return new Rfake(); } |
Please ignore the method argument. This is a trace of the previous implementation.
Fitted method
The Fitted() method is defined at the 20th line of the template. This is an abstract method declared in the Rtype class definition and must be implemented in an Rtype-derived class.
The Fitted getter, mentioned later, refers to the Fitted() and returns the instance initialization status.
We define the initialization state of the fake class as follows:
An instance has been initialized if the InternalData field is not null.
We modify the template for the sake of the fake class.
24: | public override Rbool Fitted(VirtualMachine vm) { return new Rbool(InternalData != null); } |
Please ignore the method argument. This is a trace of the previous implementation.
The fake class has internal data, so the initialization state makes sense. However, there are some classes whose initialization state is meaningless.
In such cases, you should implement the Fitted() method returns always Rbool(true).
Fixed code
The entire modified code above is shown below.
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"; // Class name as Rice class. |
13: | private string InternalData = null; // Internal data. |
14: | |
15: | public Rfake() { _typename = TYPENAME; } // Default constructor. |
16: | |
17: | public Rfake(string val) { // A constructor that initializes the internal data. |
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); } |