Try OpenEdge Now
skip to main content
GUI for .NET Programming
Accessing and Managing .NET Classes from ABL : Defining ABL-extended .NET objects : Implementing .NET interfaces in ABL
 

Implementing .NET interfaces in ABL

As with overriding methods of an inherited .NET class (see Overriding.NET methods), when you implement a method of a .NET interface, you must define the method in ABL exactly as specified by the .NET method prototype. This means that the name, return type, and the signature of the implementing ABL method must match the .NET method prototype with respect to the number of parameters, the corresponding parameter modes, and the corresponding data types. Define the method parameters exactly as you do for the return values and parameters of ABL-overridden .NET methods (see Overriding.NET methods). See Table 2 for a summary of the same rules for defining data types and parameter modes for ABL-overridden .NET methods.
When you implement a property of a .NET interface, you must define the ABL property with an implementation that is compatible with the specified .NET property prototype. So, your ABL property must define the same property name, the same .NET data type, and a compatible pattern of GET and SET accessors. If the .NET property prototype has a C# get, the implementing ABL property must have a GET accessor, and if the .NET property prototype has a C# set, the implementing ABL property must have a SET accessor. However, if the .NET property prototype specifies only one accessor, the implementing ABL property can add an implementation for the missing one. If the data type of the interface property prototype is a .NET mapped data type, you must define the ABL property data type using the rules for explicitly mapping .NET data types. For more information, see Explicit data type mappings.
When you implement an event of a .NET interface, you must define the event exactly as specified by the .NET event prototype, defining the same event name and signature. To define the same signature as a .NET event prototype, you must use the DELEGATE option of the ABL DEFINE EVENT statement to specify the same .NET delegate type that is used to define the event prototype. For more information on .NET delegate types, see Handling .NETevents.
As with ABL, all .NET interface members are PUBLIC.
For example, if you wanted to implement the .NET System.Collections.ICollection interface, this is the C# declaration for it:
public interface System.Collections.ICollection :
   System.Collections.IEnumerable /* Inherited interface */
{   
int Count { get; }               /* Property */
   bool IsSynchronized { get; }     /* Property */
   System.Object SyncRoot { get; }  /* Property */

   void CopyTo (                    /* Method */
     System.Array array,
     int index
   )
}
This is the C# declaration for the inherited System.Collections.IEnumerable interface:
public interface System.Collections.IEnumerable
{
   System.Collections.IEnumerator GetEnumerator ( )   /* Method */
}
Finally, this is the C# declaration for the System.Collections.IEnumerator interface, the object type returned by the GetEnumerator( ) method:
public interface System.Collections.IEnumerator
{
   System.Object Current { get; }  /* Property */

   bool MoveNext ( )               /* Method */
   void Reset ( )                  /* Method */
}
The following ABL CustNameCollection class then implements the ICollection and IEnumerable interfaces:
USING Progress.Lang.* FROM PROPATH.
USING System.Collections.* FROM ASSEMBLY.
ROUTINE-LEVEL ON ERROR UNDO, THROW.

CLASS CustNameCollection IMPLEMENTS ICollection, IEnumerable :
  DEFINE PUBLIC PROPERTY Count AS INTEGER NO-UNDO
    GET( ):
      RETURN THIS-OBJECT:Count.
    END.
    PRIVATE SET.

  DEFINE PUBLIC PROPERTY IsSynchronized AS LOGICAL NO-UNDO
    GET( ):
      UNDO, THROW NEW System.NotImplementedException( ).
    END.

  DEFINE PUBLIC PROPERTY SyncRoot AS System.Object NO-UNDO
    GET( ):
      UNDO, THROW NEW System.NotImplementedException( ).
    END.

  METHOD PUBLIC VOID CopyTo(
    pArray AS System.Array,
    pIndex AS INTEGER ):
      UNDO, THROW NEW System.NotImplementedException( ).
  END METHOD.

  METHOD PUBLIC IEnumerator GetEnumerator( ):
    RETURN NEW CustNameEnumerator(OUTPUT Count).
  END METHOD.
END CLASS.
In this case, while all the properties and methods of these interfaces are implemented, only the Count property of the ICollection interface and the GetEnumerator( ) method of the IEnumerable interface are implemented with any functional behavior. The remaining members of CustNameCollection throw the .NET System.NotImplementedException object when accessed, because this is the standard exception to throw in .NET when you do not implement functionality for a member of an interface or in an overridden method.
The GetEnumerator( ) method returns an IEnumerator object implemented by the ABL CustNameEnumerator class, which creates an ABL query on the Customer table of the Sports2000 database. The constructor returns the number of records in the opened query as an OUTPUT parameter and the GetEnumerator( ) method stores the result in the Count property.
Note: While implementing these interfaces does allow you to access ABL data through a standard .NET mechanism, you typically use data binding to associate data with a class in .NET. For more information on using data binding in ABL see BindingABL Data to .NET Controls.
The following ABL CustNameEnumerator class implements the IEnumerator interface:
USING System.Collections.* FROM ASSEMBLY.

CLASS CustNameEnumerator IMPLEMENTS IEnumerator :
  DEFINE PRIVATE QUERY qCust FOR Customer SCROLLING.
  
  DEFINE PUBLIC PROPERTY Current AS System.Object NO-UNDO
  GET( ):
    GET CURRENT qCust NO-LOCK.
    THIS-OBJECT:Current = IF AVAILABLE(Customer) THEN Customer.Name ELSE ?.
    RETURN THIS-OBJECT:Current.
  END.
  PRIVATE SET.

  METHOD PUBLIC LOGICAL MoveNext( ):
    GET NEXT qCust NO-LOCK.
  END METHOD.

  METHOD PUBLIC VOID Reset( ):
    GET FIRST qCust NO-LOCK.
    GET PREV qCust NO-LOCK.
  END METHOD.

  CONSTRUCTOR CustNameEnumerator(OUTPUT piCount AS INTEGER):
    OPEN QUERY qCust PRESELECT EACH Customer.
    piCount = QUERY qCust:HANDLE:NUM-RESULTS.
  END CONSTRUCTOR.
END CLASS.
This class implements all the members of the IEnumerator interface, which allow you to get the value of the Name field returned by a private query data member from a given record of the Customer table. The Current property returns the current Name value or the Unknown value (?) if the query has just been opened, is at the end, or has been reset to the beginning by the Reset( ) method, and the MoveNext( ) method gets the next record in the query. Note that the class constructor uses the NUM-RESULTS attribute on the query handle to return the value for the Count property of ICollection. Thus, all the implemented .NET properties and methods function and interpret ABL data in a manner that is understandable in the .NET context.