Meta-Properties are special purpose keys which may be used within a Dictionary to control how it is instantiated. Most importantly, this is how a Dictionary becomes an object of your choosing instead of a C# Dictionary or Hashtable. Let's explore all the meta-properties.

The _class Meta-Property

This meta-property declares the actual C# type which should be created from this Dictionary. When this meta-property is present in a Dictionary, the Anise engine will create an instance of that class, and interpret other Pairs in that Dictionary as either property assignments or method calls. For example, suppose we had the following C# class declaration:

    1: namespace MyWidgetMachine.Core {
    2:     public class Widget {
    3:         public string Type = { get; set; }
    4:         public void Crank(int times) {
    5:             Output.WriteLine("The " + this.Type + " widget was "
    6:                 + "cranked " + times + " times!");
    7:         }
    8:     }
    9: }

Then, the following Anise script would define an instance of that class, assign a value to a property, and invoke a method:

    1: myWidget = {
    2:     _class = MyWidgetMachine.Core.Widget;
    3:     Type = shiny;
    4:     Crank = [ 5 ];
    5: };

This example, if instantiated, would create the requested instance and print "The shiny widget was cranked 5 times!" on the console as a side-effect. Method invocations always require an Array be passed containing the appropriate arguments for that method. In the case of overloaded method, the exact method to call is resolved by the number of arguments. This means that in order to invoke a method, there must be exactly one method with that name and number of arguments in the class (more on the reason for this restriction in the section on the "#converter" preprocessor directive). If a method has no arguments, an empty Array should be provided.


Ordinarily, when you define a key multiple times, only the last assignment will actually be used by Anise. For example, in the following Anise script, the alpha object will have the value "charlie" when created:

    1: alpha = "alpha"
    2: alpha = "bravo";
    3: alpha = "charlie";

Anise does not attempt to create objects until they are called for, so neither "alpha" nor "bravo" were ever assigned to alpha . So, for example, the following declaration would cause only "charlie" to be added to the list because the assignment for "alpha" and "bravo" were overridden by the declaration for "charlie" before myList is even created:

    1: myList = {
    2:     _class = System.Collections.List;
    3:     Add = [ "alpha" ];
    4:     Add = [ "bravo" ];
    5:     Add = [ "charlie" ];
    6: };

In order to add all three items to the list, you'd need to apply a label to each declaration. In order to add a label, add a : character after the name of the desired property or method, and then supply some unique identifier (e.g., a sequence of integers is fine). So, to fix the prior example:

    1: myList = {
    2:     _class = System.Collections.List;
    3:     Add:1 = [ "alpha" ];
    4:     Add:2 = [ "bravo" ];
    5:     Add:3 = [ "charlie" ];
    6: };

Labels can be any text you like (except they cannot contain = characters). In order to refer to an object, property or method which has a label, simply include the label in your reference name. For example, a reference to @myList.Add:2.0 would refer to the string, "bravo".

The _constructor Meta-Property

This meta-property allows you to provide arguments to the constructor used to instantiate the object. If this meta-property is absent, then the default constructor will be used. There is one important limitation: the class may not have multiple constructors with the given number of arguments (e.g., if you provide two arguments, then there must be exactly one constructor for the class which accepts two arguments). The reason for this restriction is discussed later, in the #converter preprocessor section.

The _scope Meta-Property

So far, we haven't considered what the scope of an individual instance created by the Anise engine would be. Are they all singletons? Is a new one created each time it is referenced? Does the engine hold a reference to every object it creates? The answer to all these questions is: it depends upon the scope of the object. You specify the scope using the _scope meta-property. The following sections describe each of the possible values.

The invocation Scope

The default scope is invocation . This means that the container will use the same instance for a particular name through the course of a single call to the GetObject method. In a subsequent call to GetObject , a new instance will be created. Consider the following script:

    1: myWidget = {
    2:     _class = MyWidgetMachine.Core.Widget;
    3: };
    5: widgetList = [ @myWidget, @myWidget, @myWidget ];

And now, consider if we had the following C# code:

    1: List<Widget> list1 = aniseEngine.GetObject<List<Widget>("widgetList");
    2: List<Widget> list2 = aniseEngine.GetObject<List<Widget>("widgetList");

In this example, myWidget has invocation scope because it doesn't explicitly declare any other scope. Therefore, list1 contains three references to the same instance of Widget , and list2 contains three references to a completely different instance. However, only two instances of Widget were created (one for the first call to GetObject , and one for the second). Naturally, list1 and list2 are also different instances.

The container Scope

The container scope means that the same object should always be associated with a given name within a single Anise engine. No matter how many times a specific name is requested, and no matter whether in the same call to GetObject or not, the same instance will be returned. In the example given in the invocation section, if myWidget had container scope, both list1 and list2 would refer to the same instance of List , and each of them would contain three references to exactly the same instance of Widget . However, in the following code sample, list1 and list2 would again refer to different instances because each call to GetObject is on a different AniseEngine :

    1: List<Widget> list1 = firstEngine.GetObject<List<Widget>("widgetList");
    2: List<Widget> list2 = secondEngine.GetObject<List<Widget>("widgetList");

The prototype Scope

There are occasions when you would like a new instance each and every time a name is referenced; for that, use the prototype scope. This will cause the Anise engine to create a new instance each time it resolves a reference to the given object. Considering our example from the invocation section, if myWidget had prototype scope, list1 would refer to three unique instances of Widget , and list2 would again refer to three more (i.e., a total of six instances of Widget would be created).

The singleton Scope

Finally, the singleton scope provides exactly what you'd expect if you're familiar with the Singleton pattern: only one instance for a given name will be created, ever. Even in the example given in the container section, both list1 and list2 would refer to the same instance of List , and each would contain references to exactly the same instance of Widget .

Last edited Jun 28, 2010 at 1:28 AM by aminer, version 1


No comments yet.