Try OpenEdge Now
skip to main content
GUI for .NET Programming
Using .NET data types in ABL : Working with .NET generic types : Identifying constraints on generic type parameters
 

Identifying constraints on generic type parameters

The complete formal declaration for a generic type can also include constraints on the .NET data types that are allowed for each of its type parameters. These constraints place limits on the choice of data types that you can substitute for a given type parameter.
If there are no constraints on a type parameter, when a generic class is compiled, the .NET compiler treats the type parameter as a System.Object. Therefore, the methods and properties defined in the generic class can operate on the type parameter only in ways that are allowed for a System.Object, which is a very limited set of operations. By constraining the type parameter, it increases the number of allowable operations to what are supported by the constraining types and all types in their inheritance hierarchy.
Therefore, generic classes and interfaces often have constraints on their type parameters. It gives the coder of the generic type more flexibility and some type checking even before the actual type is supplied in a constructed type name.
A given constraint might allow you to substitute only .NET value types for a given type parameter, which would allow you to substitute the ABL INTEGER or .NET "System.Drawing.Size" type. A type with this constraint is the SettablePropertyValue<T> generic class in the Microsoft.EnterpriseManagement.Administration namespace. Note that constraints on a generic type parameter are introduced in C# by the where keyword (shown in bold):
public sealed class SettablePropertyValue<T> where T : struct
For another example, this is a C# generic class type that constrains the TEmployee type parameter to be replaced by an Employee object or any object that inherits from Employee:
public class GenericList<TEmployee> where TEmployee : Employee
A .NET generic type parameter can also be defined with multiple constraints to identify a particular subset of .NET types that you can substitute for a given type parameter in a constructed type name. This example shows the C# definition for the System.Nullable<T> generic structure type:
public struct Nullable<T> where T : struct, new()
Thus, System.Nullable<T> defines two constraints on the type parameter T, where the .NET type you substitute for T must be a value type with a default constructor (a constructor with no parameters).
The following table lists every possible type parameter constraint using C# syntax, where the where keyword identifies one or more constraints on some type parameter, Tparm, and the description shows one or more ABL constructed type examples that satisfy each constraint.
Constraint
Description
where Tparm : struct
Tparm must be a value type, and can be any value type except a nullable type (System.Nullable).
For example, valid ABL references to a generic type, SomeGeneric<Tparm>, with this constraint can be:
"SomeGeneric<SHORT>"
  "SomeGeneric<System.Drawing.Point>"
where Tparm : class
Tparm must be a reference type, which can be any class, interface, delegate, or array type.
For example, a valid ABL reference to a generic type, SomeGeneric<Tparm>, with this constraint can be:
"SomeGeneric<System.Windows.Forms.Button>"
where Tparm : new()
Tparm must have a default public constructor (without parameters)
For example, a valid ABL reference to a generic type, SomeGeneric<Tparm>, with this constraint can be:
"SomeGeneric<System.Windows.Forms.Label>"
where Tparm : BaseClassName
Tparm must be, or derive from, the class specified by BaseClassName.
For example, a valid ABL reference to a generic type defined as SomeGeneric<TControl> where TControl : System.Windows.Forms.Control can be:
"SomeGeneric<System.Windows.Forms.ButtonBase>"
where Tparm : InterfaceName
Tparm must be, or must implement, the interface specified by InterfaceName. The constraining interface can also be generic.
For example, a valid ABL reference to a generic type defined as SomeGeneric<TICollection> where TICollection : System.Collections.ICollection can be:
"SomeGeneric<System.Collections.Queue>"
where Tparm : Uparm
The type argument that you substitute for Tparm must be, or must derive from, the type argument that you substitute for another type parameter, Uparm. This is called a naked type constraint.
For example, a valid ABL reference to a generic type defined as SomeGeneric<TValue1, TValue2> where TValue1 : TValue2 can be:
"SomeGeneric<System.Windows.Forms.ButtonBase, System.Windows.Forms.Control>"
where Tparm : constraint
[ , constraint]...
Tparm can by constrained by one or more of the previously listed constraints (constraint), as long as the new() constraint is listed last.
For example, a valid ABL reference to a generic type defined as SomeGeneric<TValue> where TValue : class, new() can be:
"SomeGeneric<System.Windows.Forms.Label>"