7.7 The specification that the scripting language accesses C#

The Limax provides limax.util.ReflectionCache class to give the possiblely complete support for script language to access C# object. Currently it is used by CLR/Lua and CLR/Javascript.


  • 7.7.4 Property access

    • Read properties

      When GetValue, if the parameter of name is string type and the value matches the current object's property name, execute the property reading. If the property can’t be gotten, return DBNull.Value.


    • Write properties

      When SetValue, if the parameter of name is string type and the value matches the current object's property name, use value to write property. The type of value provided by the scripting language might not match the actual type of the property, so the type conversion must be executed firstly. There is possible to throw exception when type conversion fails. If property can’t be set, the operation is ignored.


  • 7.7.5 Method access

    • Read methods

      When GetValue, if the parameter of name is string type and the value matches the current object's method name, use an internal object which implements the Invokable interface to encapsulate the method set (method override allows the existance of the methods with the same name) and return.


    • Write methods

      When SetValue, if the parameter of name is string type and the value matches the current object's method name, this operation is ignored.


  • 7.7.6 Indexed property access

    The syntax of the scripting language limits the access to the single-parameter indexed property only.

    • Read indexed properties

      When GetValue, if the parameter of name is string type and the value of name does not match any accessable field name, property name and method name, assume the name as the index parameter to execute the following access, and such access is ambiguous. If the parameter of name is the other type, directly recognize the as the index parameter to traverse all single-parameter readable index properties. When matches the type, execute the access; without a match, return DBNull.Value. The access way is the same as the method call and will be introduced later. There might be exception thrown.


    • Write indexed properties

      When SetValue, if the parameter of name is string type and the value of name does not match any accessable field name, property name and method name, assume the name as the index parameter to execute the following access, and such access is ambiguous. If the paramter of name is the other type, directly recognize the name as the index parameter to traverse all single-parameter writable index properties. When matches the property, execute the access; without a match, ignore the operation. The access way is the same as the method call and will be introduced. There might be exception thrown.


  • 7.7.7 Event access

    The common scripting languages have no relevant syntax to correspond, so they are not supported.


  • 7.7.8 Method call flow

    Execute Invoke on the Invokable interface object obtained through GetValue, construct an object, read indexed properties, write indexed properties, in these four cases, the method call flow is executed.

    • Parameter matching

      Traverse all methods with the same name. First of all, comparing to the length of the formal parameter list and acutal parameter list to match, then parameter-by-parameter match, and get a suitable method list. Parameter matching uses a weighted evaluation algorithm, the methods with the highest score are selected, the methods with the evaluation value as Int32.MinValue are directly discarded, and if all methods are Int32.MinValue, they are all discarded. The calculation of the method evaluation value:

      • 1. Traverse formal parameter list

        • 1.1. Use the actual parameter with the corresponding position of the formal parameter and the type of the formal parameter to calculate the evaluation value.

          • 1.1.1. If the type of actual parameter equals to the type of formal parameter, it means full matching and returns the value 1.

          • 1.1.2. If the actual parameter is the instance of the type of the formal parameter, or the actual parameter is null and the type of the formal parameter is non-value type, it means the compatible conversion and returns value 0.

          • 1.1.3. If the type of formal parameter is bool or string, return value -1, which is for compatibility with scripting language feature and can execute safe conversion.

          • 1.1.4. If both the formal parameter and actual parameter are IConvertible type, it means there is chance to use System.Convert.ChangeType to convert and numeric types are IConvertible. An exception is thrown when the conversion loses accuracy, which is the main reason why the following parameter binding process ignores the failed conversions, and this case returns value -2.

          • 1.1.5. The other cases have no valid conversion and return Int32.MinValue.

        • 1.2. If the parameter evaluation value is Int32.MinValue, the method evaluation value directly returns Int32.MinValue, otherwise it is added to the method evaluation value.

      • 2. If all actual arguments are matched, return method evaluation value.


    • Parameter binding

      Traversing the obtained method list after matching, executes the parameter type conversion, ignores the failed conversions and records the successful conversions. If there is more than one successful conversion, throw the exception System.Reflection.AmbiguousMatchException. Parameter binding, in fact is to execute the conversion for the actual argument according to the formal parameter type and obtain the new parameter list object. Conversion method:

      • 1. If the actual parameter is the instance of the formal parameter type, or the actual parameter is null and the formal parameter type is non-value type, directly return this argument, which actually corresponds to two cases that the evaluation values are 1 and 0.

      • 2. In the case that the formal parameter type is bool (the evaluation value is -1), convert the actual parameter following the conventions of the scripting language:

        • 2.1. If the actual parameter is null or DBNull.Value, convert to false.

        • 2.2. If the actual parameter is char, convert to false when it equals to '\0', otherwise convert to true.

        • 2.3. If the actual parameter is string, convert to false when the lenght is 0, otherwise convert to true.

        • 2.4. If the argument is numeric type, the System.Convert.ChangeType executes the conversion, and this conversion does not throw exception and obtain the result which is in accordance with the scripting language conventions (Of cause, it is not the Lua's convention. For Lua, if 0 then print('true') else print('false') end outputs true, which is not compatible with Lua).

        • 2.5. The other cases are converted to true.

      • 3. When the formal parameter type is string (the evaluation value is -1), use the provided toString delegation when constructing ReflectionCache to submit the actual argument to the script system to convert.

      • 4. For the other cases (the evaluation value is -2), the System.Convert.ChangeType executes the conversion and might throw the exception, which means the corresponding method of the parameter list can not be called and should be ignored.


    • Method call

      If there is successful conversion, use the conversion result to execute the corresponding method; if not, for the Invoke on the Invokable object, throw exception which means no suitable method to use; for the construct object, throw the exception which means no suitable construct function to use; return DBNull.Value when reading indexed property and ignore this operation when writing indexed property. If the return type of the Invoke method on the Invokable object is void, return DBNull.Value.


    • Matters need attention

      • 1. Not support the methods with optional parameters, which means when calling the methods with optional parameters, these optional parameters can not be omitted. The methods with optional parameters are not commonly used, so the performance of the version which supports the optional parameters will sharply reduced and is more harm than good.

      • 2. The parameter matching is the pretreatment actually to reduce the number of parameter binding failure and improve the performance.

      • 3. In implementation, first perform the matching according to the parameter list length. If the unique method is choosen, omit the parameter matching process to improve the performance. So reducing the usage of the overriding is the best way.

      • 4. In the parameter evaluation algorithm, in order to to reduce the probability of parameter binding failure, the evaluation value converted to string is higher than an inter-numeric conversion, some cases might break the script usage habit. For example, define the methods int add(int a, int b); and string add(string a, string b); , to respectively perform the numeric addition and string concatenation. The scripting language calls add(1,2), returns 3 if javascript calls, and returns 12 if Lua calls. The reason is that the Javascript's integer is int which could strictly match and the evaluation values are 2 and -2. The Lua's integer is long and the evaluation values are -4 and -2, which should be careful.

      • 5. The parameter binding is a fully dynamic process. For example, the scripting language uses a variable as the actual parameter to call the method func(short x);. If the variable value is within the short range, this method is called, otherwise report that could not find a suitable method.


    • Properly handle delegation

      When C# object enters the script environment, it needs to check whether the object is delegation object. If it is, further GetValue on this object to obtain the Invokable encapsulation of the Invoke method as the object which is really referenced by script. From the script environment, this object has no any different with the Invokable encapsulaton obtained when reading the object method. If the Invokable encapsulation returns to C#, it must be further judaged whether the encapsulated object is the delegation object. If it is, return the delegation object, otherwise return the encapsulation itself. This keeps the consistence and is the function that the Invokable.GetTarget() method needs to finish.

      The delegation object must be encapsulated. The reason is that the implementation of the .Net provides a Delegate base class, and the information defined by the delegate keyword is used to generate a class and this class inherits from the Delegate. The formal parameter list of the generated Invoke method is the formal parameter list defined by the delegate. With this list, the correct type conversion can be carried and called. If no encapsulation and directly call Delegate.DynamicInvoke, it lacks of the type conversion process and can not guarentee the correctness.


Prev Next