Generic types (.NET)
Generic types are only supported in Synergy .NET.
A generic type enables you to define a class, structure, method, delegate, or interface that can work with data of various types. You don’t have to specify the actual data type(s) until an instance of the generic type is created, and the actual types can differ for different instances of the same generic type. These type-safe data structures can help you optimize your code.
Declaring a generic class
To declare a generic class, use the following syntax:
class name<T>
Then add member fields that use the generic type to the class declaration in the format
name ,T
or
name @class<T>
Any unused letter can be used for the type parameter, with restrictions specified in the description for T. For example, in the following generic class declaration, Q is the type parameter.
class MyClass<Q> public myfield ,Q endclass
Synergy .NET enables you to specify restrictions on the kinds of types that can be used for type arguments when a class is instantiated. A constraint tells the compiler that only objects of the specified type (or derived from the specified type) will be used as type arguments. Constraints are specified as follows:
class name[<T[(constraints)], ...>
A class’s constraints can consist of any combination of the following:
- A base class. Restricts the type parameter to a class that inherits from a certain base class. By default, all type parameters have a base class of System.Object.
- One or more interfaces. Restricts the type parameter to classes that implement these interfaces.
- The keyword “new”, which indicates a constructor. Allows you to create an instance of the type parameter.
In the following example, the generic type parameter is restricted to a class that inherits from SomeBaseClass.
class MyClass<T(SomeBaseClass)>
In the example below, the generic type parameter is restricted to classes that implement Interface1 or Interface2:
class MyClass<T(Interface1,Interface2)>
You can optionally declare one or more constraints on each type parameter within a generic declaration. For example,
class MyGeneric<T(class1,iface1,iface2,new),S(class2),K(iface3),X(new)>
To indicate that the type parameter is constrained to an object only, use the keyword class. When this constraint is explicitly specified, no value types are allowed on construction. For example,
public MyClass <T(class)>
To indicate that the type parameter is constrained to a CLS structure (i.e., a value type) only, use the keyword structure. You can only specify one structure constraint. For example,
public MyStructure <T(structure)>
You can also use a type parameter within a constraint in other type parameters in the same generic declaration. In the example below, type parameter T is used in the constraint for type parameter S:
class MyGeneric<T(class1), S(T)> class MyGeneric<T(S), S(class1)>
The advantage of using constraints on a type parameter when declaring a generic class is that the generic declaration can then perform functionality available to the constraint within the generic. For example, with the following declaration:
interface iface1 method test, void endmethod endinterface
you can use the declared method (test) in the generic class declaration, as follows:
class mygeneric<T(iface1)> public fld1, T public method doit, void proc fld1.test() end endclass
Although fld1 is of type T, T is assumed to be implementing iface1, so the compiler allows you to use iface1 members when accessing something of type T. This applies to both a specific class constraint and the new constraint.
The new constraint can only coexist with the class constraint.
If you declare the constructor constraint on a type parameter, the calling program can create an instance of that type. For example, if a generic class has the following declaration:
class myclass<T(new)> public method mymethod, void p1, T
then within mymethod, you can create an instance of the generic type as follows:
p1 = new T()
When the new constraint is used alongside a base class constraint, if the base class has writable fields or properties, an object initializer can be used to initialize those fields or properties:
p1 = new T() { myProperty = 1 }
Constructed types and usage
You can create a constructed type from a generic class or structure by providing generic type arguments that meet the constraints for each of the generic type parameters. For example, given the following generic class:
class MyGenericClass<T>
the constructed type MyGenericClass<int> is created:
record c1, @MyGenericClass<int> proc c1 = new MyGenericClass<int>()
All but the following are allowed as type arguments: delegates, enumerations, Synergy descriptor types (a, d, d., I, i1-i8, n, p, or p.), or real arrays of Synergy descriptor types. An “@” is not allowed in a type argument. For instance, the following declaration causes a NOTALLOWED error:
list2, @class1<@string>
When accessing a static member of a generic class, you can specify a type argument for each of the type parameters when accessing that static member. For example, if a static method uses the generic type K in its declaration, as shown below:
class MyClass<K> public static method mymethod, void p1, K endmethod endclass
you could call it as follows:
Myclass<int>.mymethod(5)
A static field in a generic class is shared between all instances of the same constructed type, but it is not shared between all different versions of constructed types. In the following example, var1 and var2 share static fields of MyGeneric<int>, but var3 does not:
record var1, @MyGeneric<int> var2, @MyGeneric<int> var3, @MyGeneric<long>
Using a generic class
To use a generic class, add code to instantiate the generic class and provide another type as a substitute for the type parameter. This substitute type must meet all of the type parameter constraints declared in the generic class. For example,
c1, @MyClass<SomeOtherClass> c1 = new MyClass<SomeOtherClass> ()
You can then use the instantiated class just like any other normal Synergy .NET class.
Generic methods, delegates, and interfaces
Methods, delegates, and interfaces can also be generic. See METHOD-ENDMETHOD, DELEGATE-ENDDELEGATE, and INTERFACE-ENDINTERFACE for syntax.
The number of type parameters for a generic method, delegate or interface determines the method signature or uniqueness of that item. If you specify two generics whose method signatures or declarations differ only by the number of type parameters, those generics are considered unique. If two generics in the same scope have the same signature, which includes the number of type parameters, the compiler reports a “Duplicate method” error. (Note that the names and constraints of the type parameters are not considered for uniqueness.)
A generic method is not considered to be a generic type. |
You can declare the type parameter, either directly or as part of a constructed type, as the type for the following items in the declaration of each generic:
Generic |
Items for which a type parameter can be declared |
---|---|
Methods |
|
Delegates |
|
Interfaces |
|
If a generic method pointer is passed into a delegate and the type arguments for the generic method can be deduced from the signature of the delegate, Synergy DBL adds the implicit type arguments.