Ancestor and descendent variables
All objects in PowerBuilder are descendants of PowerBuilder system objects–the
objects you see listed on the System page in the Browser.
Therefore, whenever you declare an object instance, you are
declaring a descendant. You decide how specific you want your declarations
to be.
As specific as possible
If you define a user object class named uo_empdata,
you can declare a variable whose type is uo_empdata to
hold the user object reference:
1 |
uo_empdata uo_emp1 |
1 |
uo_emp1 = CREATE uo_empdata |
You can refer to the variables and functions that are part
of uo_empdata’s definition because uo_emp1’s
type is uo_empdata.
When the application requires flexibility
Suppose the user object you want to create depends on the
user’s choices. You can declare a user object variable
whose type is UserObject or an ancestor class for the user object.
Then you can specify the object class you want to instantiate in
a string variable and use it with CREATE:
1 |
uo_empdata uo_emp1 |
1 |
string ls_objname |
1 |
ls_objname = ... // Establish the user object to open |
1 |
uo_emp1 = CREATE USING ls_objname |
This more general approach limits your access to the object’s
variables and functions. The compiler knows only the properties
and functions of the ancestor class uo_empdata (or the
system class UserObject if that’s what you declared). It
doesn’t know which object you will actually create and
cannot allow references to properties defined on that unknown object.
Abstract ancestor object In order to address properties and functions of the descendants
you plan to instantiate, you can define the ancestor object class
to include the properties and functions that you will implement
in the descendants. In the ancestor, the functions do not need code
other than a return value–they exist so the compiler will
recognize the function names. When you declare a variable of the
ancestor class, you can reference the functions. During execution,
you can instantiate the variable with a descendant, where that descendant
implements the functions as appropriate:
1 |
uo_empdata uo_emp1 |
1 |
string ls_objname |
1 |
// Establish which descendant of uo_empdata to open |
1 |
ls_objname = ... |
1 |
uo_emp1 = CREATE USING ls_objname |
1 |
// Function is declared in the ancestor class |
1 |
result = uo_emp1.uf_special() |
This technique is described in more detail in “Dynamic versus static lookup”.
Dynamic function calls Another way to handle functions that
aren’t defined for the declared class is to use dynamic
function calls.
When you use the DYNAMIC keyword in a function call, the compiler
doesn’t check whether the function call is valid. The checking
happens during execution when the variable has been instantiated
with the appropriate object:
1 |
// Function not declared in the ancestor class |
1 |
result = uo_emp1.DYNAMIC uf_special() |
Performance and errors You should avoid using the dynamic capabilities of PowerBuilder when
your application design does not require it. Execution time evaluation
means that work the compiler usually does must be done at execution
time, making the application slower when dynamic calls are used
often or used within a large loop. Skipping compiler checking also
means that errors that might be caught by the compiler aren’t
found until the user is executing the program.
Dynamic object selection for windows and visual
user objects
Opening a window or visual user object is done with a function
call instead of the CREATE statement. With the Open and OpenUserObject
functions, you can specify the class of the window to be opened,
making it possible to open a descendant different from the declaration’s
object type.
This example displays a user object of the type specified
in the string s_u_name and stores the reference
to the user object in the variable u_to_open. U_to_open
is of type DragObject, which is the ancestor of all user objects.
It can hold a reference to any user object:
1 |
DragObject u_to_open |
1 |
string s_u_name |
1 |
s_u_name = sle_user.Text |
1 |
w_info.OpenUserObject(u_to_open, s_u_name, 100, 200) |
For a window, comparable code looks like this. The actual
window opened could be the class w_data_entry
or any of its descendants:
1 |
w_data_entry w_data |
1 |
string s_window_name |
1 |
s_window_name = sle_win.Text |
1 |
Open(w_data, s_window_name) |