Feature
|
Microsoft .NET C#
|
OpenEdge GUI for .NET ABL
|
Object type-name syntax
|
C# Syntax
namespace . dotNET-object-name
.inner-type-name
Example .NET type names:
System.Windows.Forms.Control System.Windows.Forms.Control.ControlCollection System.Drawing.Point[] |
ABL Syntax
"
namespace . dotNET-object-name +inner-type-name "
Example .NET type names:
System.Windows.Forms.Control System.Windows.Forms.Control+ControlCollection "System.Drawing.Point[]" |
Allowing unqualified type references
|
C# Syntax
using namespace;
Example:
// Unqualified reference to // all types in a namespace using System.Windows.Forms; |
ABL Syntax
USING namespace.* FROM ASSEMBLY . USING namespace.type-name FROM ASSEMBLY .
Examples:
/* Unqualified reference to all types in a namespace */ USING System.Windows.Forms.* FROM ASSEMBLY. /* Unqualified reference to the Button class type */ USING System.Windows.Forms.Button FROM ASSEMBLY. |
Class instantiation
|
C# example:
using System.Windows.Forms; Button rButton = new Button( ); |
ABL example:
USING System.Windows.Forms.* FROM ASSEMBLY. DEFINE VARIABLE rButton AS Button. rButton = NEW Button( ). |
Type casting1
|
C# example:
using System.Windows.Forms; Control rControl = new Control( ); Button rButton = new Button( ); rControl = rButton; rButton = (Button) rControl; |
ABL example:
USING System.Windows.Forms.*. DEFINE VARIABLE rControl AS Control. DEFINE VARIABLE rButton AS Button. rControl = NEW Control( ). rButton = NEW Button( ). rControl = rButton. rButton = CAST ( rControl, Button ). |
Instance member reference
|
C# Syntax
object-reference.member-reference
Example:
using System.Windows.Forms; Button rButton = new Button( ); rButton.Left = 10; /* Property ref. */ rButton.Focus( ); /* Method call */ |
ABL Syntax
object-reference:member-reference
Example:
USING System.Windows.Forms.*
FROM ASSEMBLY. DEFINE VARIABLE rButton AS Button. rButton = NEW Button( ). rButton:Left = 10. /* Property ref. */ rButton:Focus( ). /* Method call */ |
Static member reference
|
C# Syntax
object-type-name.member-reference
Example:
using System.Windows.Forms;
MessageBox.Show( System.Math.PI.ToString( ) ); |
ABL Syntax
object-type-name:member-reference
Example:
USING System.Windows.Forms.*
FROM ASSEMBLY. MessageBox:Show( STRING(System.Math:PI) ). |
Creating and using .NET array objects
|
C# example:
using System.Windows.Forms;
Button[] btnArrayObj = new Button[ 3 ]; btnArrayObj[0] = new Button( ); |
ABL examples:
USING System.Windows.Forms.*
FROM ASSEMBLY. USING Progress.Util.* FROM ASSEMBLY. /*Creating and using a .NET array object directly */ DEFINE VARIABLE btnArrayObj AS "System.Array". btnArrayObj = System.Array:CreateInstance( TypeHelper:GetType( "Button" ), 3 ). btnArrayObj:SetValue( NEW Button( ), 0 ). /* Creating and using an ABL object array to create a .NET array object by mapping */DEFINE VARIABLE btnArrayObj AS "Button[]". DEFINE VARIABLE btnArray AS Button EXTENT 3. btnArray[1] = NEW Button( ).btnArrayObj = btnArray. |
Class definition2
|
C# sample:
using System.*;
using System.Windows.Forms; using System.Collections; using System.Windows.Forms.Layout; public class Control.ControlCollection : ArrangedElementCollection, IList, ICollection, IEnumerable, ICloneable { . . . } |
ABL equivalent:
USING System.*.
USING System.Windows.Forms.*. USING System.Collections.*. USING System.Windows.Forms.Layout.*. CLASS Control+ControlCollection INHERITS ArrangedElementCollection IMPLEMENTS IList, ICollection, IEnumerable, ICloneable : . . . END CLASS. |
Method calling and overriding in class hierarchy3
|
Example inherited C# method:
protected virtualbyte[] DoOp1 (
decimal x, refshort y, outulong z ) { . . . }
Example C# usage of inherited .NET DoOp1( ) method:
// Calling DoOp1 within hierarchy
byte[] arr; decimal x; short y; ulong z; arr = DoOp1( x, ref y, out z ); // Overriding DoOp1 public override byte[] DoOp1 ( decimal x, refshort y, outulong z ) { . . . } |
Example ABL usage of inherited .NET DoOp1( ) method:
/* Calling DoOp1 within hierarchy */
DEFINE VARIABLE arr AS INTEGER EXTENT NO-UNDO. DEFINE VARIABLE x AS DECIMAL NO-UNDO. DEFINE VARIABLE y AS INTEGER NO-UNDO. DEFINE VARIABLE z AS DECIMAL NO-UNDO. arr = DoOp1( INPUT x, INPUT-OUTPUT y, OUTPUT z ). /* Overriding DoOp1 */ METHOD PUBLIC OVERRIDE "System.Byte[]" DoOp1( INPUT x AS DECIMAL, INPUT-OUTPUT y AS SHORT, OUTPUT z AS UNSIGNED-INT64 ) : . . . END METHOD. |
Event declaration and subscription
|
C# event declaration and delegate for the .NET FormClosing event:
using System.Windows.Forms;
// Event declaration public event FormClosingEventHandler FormClosing; // Event delegate public delegate void FormClosingEventHandler ( System.Object sender, FormClosingEventArgs e )
C# example event subscription and handler:
using System.Windows.Forms;
Form rFrm = new Form( ); . . . // Subscribe handler to event rFrm.FormClosing += new Form.FormClosingEventHandler( rFrm_FormClosing); . . . private void rFrm_FormClosing( System.Object sender, FormClosingEventArgs e ) { . . . } |
ABL example event subscription and handler:
USING System.Windows.Forms.*
FROM ASSEMBLY. DEFINE VARIABLE rFrm AS Progress.Windows.Form NO-UNDO. rFrm = NEW Form( ). . . . /* Subscribe handler to event */ rFrm:FormClosing:Subscribe( rFrm_FormClosing ). . . . METHOD PRIVATE VOID rFrm_FormClosing( sender AS System.Object, e AS FormClosingEventArgs ) : . . . END METHOD. |
Publishing an event in a derived class
|
.NET method that fires a FormClosing event:
protected virtual void OnFormClosing
( FormClosingEventArgs e )
Example of publishing the event in a C#-derived .NET class:
using System.Windows.Forms;
FormClosingEventArgs rClosingArgs = new FormClosingEventArgs( CloseReason:UserClosing, FALSE ); . . . /* Publish FormClosing event */ OnFormClosing( rClosingArgs ); |
Example of publishing the event in an ABL-derived .NET class:
USING System.Windows.Forms.*
FROM ASSEMBLY. DEFINE VARIABLE rClosingArgs AS FormClosingEventArgs NO-UNDO. rClosingArgs = NEW FormClosingEventArgs( CloseReason:UserClosing, FALSE ). . . . /* Publish FormClosing event */ OnFormClosing( rClosingArgs ). |
Enumeration types and operations
|
In .NET, you can operate on enumeration types using the same operators defined for integral primitive types. For example, an operator such as + is overloaded for both the C# int and enumeration types.
C# use of an enumeration type:
using System.Windows.Forms;
Button rBtn = new Button( ); rBtn:Anchor = AnchorStyles.Bottom & AnchorStyles.Left; |
In ABL, there is no operator overloading and .NET enumerations are treated only as class-based objects. To support operations on .NET enumerations, OpenEdge provides Progress.Util.EnumHelper, a .NET helper class that defines the following static methods you can use to operate on enumeration objects as System.Enum types: Add( ), And( ), AreEqual( ), AreNotEqual( ), Complement( ), IsGreater( ), IsGreaterOrEqual( ), IsLess( ), IsLessOrEqual( ), Or( ), Subtract( ), and Xor( )
ABL use of an enumeration type:
USING System.Windows.Forms.*.
USING Progress.Util.*. DEFINE VARIABLE rBtn AS Button. rBtn = NEW Button( ). rBtn:Anchor = CAST( EnumHelper:AND ( AnchorStyles:Bottom, AnchorStyles:Left ), AnchorStyles ). |
Indexed properties
|
.NET has a concept both of non-default indexed properties and default properties (also referred to as indexers or indexed properties). Virtually all indexed properties in the .NET Framework are indexers defined with the default property name Item.
The following code shows a sample C# indexer declaration you might find in .NET documentation for an Item property, in this case on the ControlCollection class:
using System.Windows.Forms;
// C# indexer declaration public virtual Control this [ int index] { get; }
Example C# indexer access on a ControlCollection object:
Control rCtrl;
ControlCollection rCol = new ControlCollection( . . . ); . . . rCtrl = rCol[ 1 ]; |
ABL documentation refers to .NET non-default indexed properties simply as indexed properties and refers to .NET default properties (or indexers) as default indexed properties. However, most indexed properties you are likely to access are default indexed properties.
Where C# considers the indexer to be the indexed property, ABL refers to an indexer as the subscript syntax used to access a default indexed property, which you can access in one of two ways. In C#, you can only access a default indexed property using an indexer on a reference to a class instance. However, ABL allows you to access a default indexed property using the indexer either on the property name (Item) or on a reference to the class instance.
Example ABL default indexed property access on a ControlCollection object:
USING System.Windows.Forms.*.
DEFINE VARIABLE rCtrl AS Control NO-UNDO. DEFINE VARIABLE rCol AS Control+ControlCollection NO-UNDO. rCol = NEW Control+ControlCollection( . . . ). /* Next two lines are equivalent */ rCtrl = rCol[ 1 ]. rCtrl = rCol:Item[ 1 ]. |
Boxing and unboxing support
|
Boxing is the process of converting a value type (such as a C# int or .NET System.Int32) to a reference type object. Boxing a value type wraps the value inside a System.Object. Unboxing extracts the value type from the object. Boxing and unboxing between a value type and System.Object can occur during assignment or parameter passing.
In C#, boxing is done automatically but can be done explicitly. Unboxing can only be done explicitly by casting from the System.Object to the value type you want to unbox.
C# examples:
int iNetVal = 99;
int[] iNetArr = int[3]; System.Object rBox; rBox = iNetVal; iNetVal = (int) rBox; iNetVal = (System.Int32) rBox; |
ABL provides three basic levels of automatic boxing and unboxing support:
Conversion between ABL primitive types (such as INTEGER) and a .NET System.Object when you assign between them
Conversion from an ABL array of compatible types (including ABL primitive and .NET object type elements) to a .NET System.Object when you pass the ABL array to a .NET method parameter defined as System.Object or when you assign the ABL array to a System.Object
Conversion between compatible ABL arrays and .NET array objects for both assignment and .NET method parameter passing. Arrays are boxing-compatible between: 1) ABL arrays of primitive types and .NET arrays of corresponding implicitly mapped types (see Implicit data type mapping between ABL primitive and .NETtypes), and 2) ABL arrays of non-mapped .NET object types and .NET arrays of type-compatible .NET object types.
In all other situations, for example, parameter passing for ABL routines and assignment from a System.Object to an ABL primitive type, automatic boxing is not supported. Where automatic boxing is not supported, you can explicitly box and unbox between these ABL and .NET types using the ABL BOX and UNBOX built-in functions, You can also use these functions where automatic boxing is supported.
Note: Automatic boxing support between ABL arrays of primitive types and .NET arrays of implicitly mapped types does not include widening support between corresponding array elements. The array elements must be implicitly mapped.
ABL examples using iNetArr from .NET:
DEFINE VARIABLE iVal AS INTEGER.
DEFINE VARIABLE iArr AS INTEGER EXTENT 3 INITIAL [33,66,99]. DEFINE VARIABLE rBox AS System.Object. rBox = iVal. iVal = rBox. rBox = iArr. iArr = UNBOX(rBox). iNetArr = iArr. iArr = iNetArr. |
Type conversion and mapping
|
In .NET, type conversion can only occur using boxing and unboxing. For more information, see the Boxing and unboxing support in this table..NET also supports aliasing between each .NET primitive type (such as C# int) and a corresponding .NET value type object (in this case, System.Int32). This allows you to assign between the primitive type and its corresponding object type and to otherwise refer to one type as the other without casting. However, this is not a type conversion, as the two types (for example, C# int and System.Int32) are really identical. Aliasing, thus, allows all .NET languages to use the same type library.
|
ABL also provides a form of boxing support to convert ABL primitive and array types to .NET object types (see the Boxing and unboxing support in this table).In addition, ABL supports a form of mapping between ABL primitive or array types and compatible .NET scalar or array types. For ABL primitive types, this mapping relies on existing .NET aliasing, but supports true type conversion between the ABL and corresponding .NET types. For more information, see Implicit data type mapping between ABL primitive and .NETtypes.4
|
Example of type aliasing in C#:
public intProperty1{get;set;} public System.Int32Property2{ . . . } Property1 = Property2; Property2 = Property1; |
Example of type mapping in ABL using Property1 and Property2 from .NET:
DEFINE VARIABLE iVal AS INTEGER.
iVal = Property1.Property1 = iVal. iVal = Property2.Property2 = iVal. |
|
Note: ABL provides built-in data type conversion functions that you can call to convert one ABL primitive type to another. For example, a call to INTEGER( "99" ) converts the string, "99", to the INTEGER value, 99. However, note that these functions are not casting operations. The value from a data type conversion function is not the same object represented as a new type, as in object type casting, but is actually a value of one primitive type converted to an equivalent value in another primitive type.
In addition, if you call an ABL data type conversion function on an object reference, ABL does not attempt to convert the value to the specified data type that the referenced object happens to represent. Instead, the function returns the ID of the referenced object as the primitive type specified by the conversion function:
MESSAGE "Object" INTEGER( y )
"has the value" y VIEW-AS ALERT-BOX.
Thus, this statement might display the following message, where 1022 is the object ID of the System.Object referenced by y:
"Object 1022 has the value 99"
Note also that when you reference a System.Object directly in a MESSAGE statement and the System.Object represents a .NET mapped type (such as a System.Int32), the statement automatically calls the ToString( ) method on the object (in this case, y:ToString( )), which returns the object's value as a string (in this case, "99") instead of its object ID.
|