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:

name[<T[(constraints)], ...>

A class’s constraints can consist of any combination of the following, unless indicated below:

class MyClass<T(SomeBaseClass)>
class MyClass<T(Interface1,Interface2)>
class MyGeneric<T(class1,iface1,iface2,new),S(class2),K(iface3),X(new)>
public MyClass<T(class)>
public MyStructure<T(structure)>
public static method MyMethod<T(allowbyref)>

One common use for allowbyref is calling an interface member on a BYREF structure. Because BYREF structures cannot be boxed, they cannot be assigned to a variable of an interface type the way a class can. Instead, a generic type parameter constrained to the interface, with allowbyref added, lets you call that interface's members on a BYREF structure argument. For example,

public static method doit<T(iface1,allowbyref)>, void
parm1, T
proc
parm1.meth1()
endmethod

See STRUCTURE-ENDSTRUCTURE for more information about structures that implement interfaces.

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.

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.