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

Constraints

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:

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.)

Note

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

  • The return type of the method
  • The parameter types of any of its parameters
  • A local variable within the method

Delegates

  • The return type of the delegate
  • The parameter types of any of its parameters

Interfaces

  • A return type or parameter type for a method
  • A property or indexer type
  • The type argument passed to a generic base interface or generic method within the interface

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.