How to use .NET Objects in DataBasic
Introduction
.NET DataBasic objects (DBOs) are opaque variables that reference external .NET language objects. They can be copied, compared and deleted like any other variable but cannot be directly used in arithmetic or string operations.
.NET objects are a subset of generic DataBasic objects. These objects are used in DataBasic as you would if writing code to run in a true .NET environment, the main exception being that the "." dot operator is replaced with the DataBasic "->" (hyphen greater-than) operator.
The objects and their associated statements are created and processed by a .NET Language Server under the control of the DataBasic run time.
To support object-orientated language syntax (for example, new) a set of special methods is available. Special methods have a % character prefix (for example, %New) to identify them from standard methods. Special method keywords are case-insensitive.
.NET languages such as C# are heavily typed, in contrast to DataBasic where variables are largely untyped. Because of this it is possible for the data type of method arguments to be ambiguous. A new 'cast' syntax has been introduced to DataBasic to alleviate this problem. A variable can be explicitly cast to a type by using a cast operator. For example, "(int16)Var
forces Var
to take the type of a 16-bit integer for the purposes of a method call. See Type Casting.
Note
The .NET Language Server distribution includes a test DLL, DBOTestDLL.dll, which is installed in the same folder as the DNLS service executable, DBOService.exe, which is normally C:\Program Files\Reality\RealDNLS64 for the 64 bit service and C:\Program Files (x86)\Reality\RealDNLS32 for 32 bit service. The classes in this assembly can be used to test the DNLS facilities. See .NET Language Server Test DLL for more information.
Connecting to a .NET Language Server
Before any .NET objects can be used or created at least one connection must be made to a .NET Language Server (DNLS) by using the %Connect special method, for example:
DotNetLS = %Connect(DOTNET)
This form of the method creates a connection object that references the DNLS on the local host. The first connection made automatically becomes the default connection, and does not have to be explicitly named thereafter. If necessary, connection objects that reference other DNLSs can be established but unlike the default connection they must be explicitly named whenever they are used.
Importing .NET assemblies
An assembly is a .NET library or collection of classes. Once a connection object has been created it should be associated with any required assemblies by using the %Import special method. An imported .NET assembly can be a simple .dll file name or a full path to the DLL. When it is a simple file name the file is expected to be in the same folder as the DNLS service.
For example:
%Import(DBOTestDLL.dll)
%Import(D:\Finances\Accountancy.dll)
Creating objects
Once there is a default connection object the %New special method can be used to create DBOs. An object is instantiated within the language server and this object is referenced by a DBO variable. The first parameter of %New is the class name, and if the class has a default constructor or a parameterless "no-arg" constructor, that is all you need. If the class has a parameterised constructor the class name can be followed by a comma-separated list of the initial values required by the constructor (or one of the constructors, if it is overloaded).
For example:
UserAccount = %New(UserProfile, "Smith", "Paul", "paul.smith@Mail4U.eu")
Here the DBO variable UserAccount references a UserProfile object that is an instantiation of the UserProfile class. The class's parameterised constructor is assumed to require surname, forename and email address strings. (The parameters do not have to be cast as strings because DataBasic can tell that they are strings.)
There is no limitation by DataBasic on the use of underlying objects. DBO variables are object references which can be passed around in the same way as normal variables and they can even be passed in and out of methods invoked on an object.
Primitive data types
The following primitive data types are recognised by the .NET Language Server.
Class name[1] |
RPC type | RPC value |
Description |
---|---|---|---|
System.Char[2] |
CHAR |
0x60 |
2 byte Unicode characters. |
System.SByte |
INT8 |
0x20 |
Signed 8 bit integer. |
System.Int16 |
INT16 |
0x30 |
Signed 16 bit integer. |
System.Int32 |
INT32 |
0x40 |
Signed 32 bit integer. |
System.Int64 |
INT64 |
0x50 |
Signed 64 bit integer. |
System.Byte |
UINT8 |
0x21 |
Unsigned 8 bit integer. |
System.UInt16 |
UINT16 |
0x31 |
Unsigned 16 bit integer. |
System.UInt32 |
UINT32 |
0x41 |
Unsigned 32 bit integer. |
System.UInt64 |
UINT64 |
0x51 |
Unsigned 64 bit integer. |
System.Single |
FLOAT |
0x70 |
32 bit floating point, +/-(10-45 to 1038). |
System.Double |
DOUBLE |
0x80 |
64 bit floating point, +/-(10-324 to 10308). |
System.Decimal |
DECIMAL |
0xC0 |
128 bit floating point, 10-28 to 1028 full precision. |
System.Boolean |
BOOL |
0x10 |
Two values – true|false (uses 1 byte of storage). |
System.String[3] |
STRING |
0xA0 |
Sequence of Unicode characters.[4] |
[1] Currently, the DNLS requires the full wrapper class name. |
The primitive wrapper classes in C# do not include methods to get or set the value of the data, so the DNLS supports an @Value special field name to access the value; for example:
Counter = %New(System.Int32) Counter->@Value = 100
(Note that primitive data type classes in C# also do not provide constructors with initial value parameters either, so something like Counter = %New(System.Int32, 100)
would not work.)
In addition, values of the System.Boolean class require casting when setting the value, for example:
IsPensioner = %New(System.Boolean) IsPensioner->@Value = (BOOL)1
Properties and fields
.NET language classes have two slightly different types of data accessor: properties and fields. The data value of a field can be read and written directly but each property is accessed by means of intermediate setters and getters (for example, get and set accessors for C#) that allow additional processing such as validation.
As far as DataBasic is concerned a property's get and set accessors are implicit, so both properties and fields are accessed identically.
Arrays
The variables contained in an array, also called the elements of the array, must all be of the same type. In addition to arrays of primitive data types, arrays of objects are also possible. Array indices start at 0. Multi-dimensional arrays are not supported.
Arrays of primitives
You can create a .NET Array object of primitive data types by applying the %Array special method to a connection object and specifying the primitive wrapper class and the initial element values — whether by a comma separated list or by a dynamic array.
The result is an Array object which can referenced by a DBO variable in the usual way. For example:
FirstTenPrimes = %Array(System.Int32, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
Similarly, an Array returned by a object method can be assigned to a DBO variable too:
MyCalculator = %New(MathCalculator) FirstTenPrimes = MyCalculator->GetPrimes(1, 10)
You can use the Array.Length property to discover the length of the array, and the @Element pseudo-field to access individual elements of the array. For example, FirstTenPrimes->@Element[5] returns 13.
Conversely when %Array() is applied to an Array object of primitive types it returns an attribute mark delimited dynamic array.
Note
The @Element pseudo-field is available only for .NET language objects; it is not available for Java language objects.
Arrays of objects
A DBO variable can refer to an Array of objects. As before the @Element pseudo-field can be used to access each one, and these can be assigned to other DBO variables. However, You cannot use %Array() to create an array of objects or convert such an array into a dynamic array.
Calling object methods
The .NET programming languages support what is referred to as method signatures or overloading whereby there may be two or methods of the same name but with different parameter lists. The DNLS can determine which method is being called from DataBasic by matching both the method name and the number and types of the parameters provided.
Static methods, properties and fields
These are methods, properties and fields that are not specific to any instantiated object of a given class but rather a persistent element of the class itself. In a .NET programming language one would reference a static element by the class name rather than the name of a particular object.
The DNLS supports a %Static special method by which DataBasic can access the static elements of a class. In the following example, MyCalculator refers to an instantiated MathCalculater object, but AnyCalculator refers to (the static elements of) the MathCalculator class itself.
MyCalculator = %New(MathCalculator) AnyCalculator = %Static(MathCalculator)
Copying an object
DataBasic cannot duplicate an external .NET language object. When a DBO variable is assigned to another DBO variable it is only the reference to the object that is copied, so that afterwards both variables reference the same object. A genuine copy is possible only if the class implements a method to do this.
Comparing two objects
Because DBO variables are only references to objects, the only meaningful comparison is equality or inequality: in other words, do these two DBO variables reference the same object, or not.
Deleting an object
Objects are automatically deleted when a DBO variable is reassigned of deleted (as in local subroutine or user function variables after the RETURN statement). In addition all objects saved in local variables are deleted when the program ends. Any DBOs saved in named common sections can be shared between program levels and are not lost when the program ends; this is not the case with data in unnamed common sections.
Object error handling
Object errors are treated as DataBasic exceptions and, once code is developed, it is recommended that an exception handler is always defined to catch any exceptions that may be thrown by the object. If no exception handler is defined, any errors generated by the DataBasic Object in run time will be treated as fatal and trapped by the DataBasic debugger.