1. Computing

Don't Check For Assigned - Ensure Your Nil Delphi Objects Return Property Values

Nobody Loves Access Violations! Have a Nil Object Return a Property Value.

By

delphi access violation in debugger

delphi access violation in debugger

delphi access violation in debugger
How many times did you write a code similar to the one below:
 if Assigned(my) then
   Caption := my.StringValue
 else
   Caption := '"my" not assigned';
 
The "my" is an object instance of some class, defined as "var my : TMySimpleObject", that could have been declared as:
 type TMySimpleObject = class
 private
   fStringValue: string;
 public
   property StringValue : string read fStringValue write fStringValue;
 end; 
The TMySimpleObject class defines a string property called "StringValue" using a private fStringValue field to write and/or read the value of the StringValue property.

If "my" object, in the example above, is not assigned and you try to access (read) its StringValue property, you will receive an Access Violation.

From my point of view, sometimes it takes to much time (lines of code) to always check if the object is assigned or not, to read its properties, and not receive access violations.

How aout simply reading my.StringValue, without the need to check if the object is nil?

Property Getters to the Rescue

With the next declaration, using property getter method, you do not need to care if an object is nil, or better to say, you would test for nil in the getter of the property:
 type TMyObject = class(TMySimpleObject)
 private
   fStringProp : string;
   function GetStringProp: string;
 public
   property StringProp : string read GetStringProp;
 end;
 
 ....
 
 function TMyObject.GetStringProp: string;
 begin
   if Assigned(Self) then
     result := fStringProp
   else
     result := 'my object is nil';
 end; 
With the above implementation of the StringProp property, you are safe to read the value of the property even when the instance of the TMyObject class is nil (not assigned):
 Caption := my.StringProp
 
No need for:
 if Assigned(my) then
   Caption := my.StringProp
 else
   Caption := '"my" not assigned';
 
Note that the getter method, GetStringProp, checks if the instance is assigned. If "Self" is not assignend, a default value is returned - in the above example: "my object is nil".

Using a getter method you can save lines of code - check if the instance is assigned in the property getter and not where you actually need the value of the property!

If Self = nil, simply return something that for you is a "default value" for non instantiated instances of a class.

Such a trick could not be used to write values to properties - as obviously for writing you need a class instance that is not nil.

In my applications I do have various classes that only expose read only properties, where their vaues are set using an overriden construtor.

What If You Need to Access Properties That Are Objects?

Here's a more complex example:
 type TMyObjectEx = class(TMyObject)
 private
   type TMySubObject = class
   private
     fMySubIntValue : integer;
     function GetMySubIntValue: integer;
   public
     property MySubIntValue : integer read GetMySubIntValue;
   end;
 private
   fSubObject: TMySubObject;
   function GetSubObject: TMySubObject;
 public
   destructor Destroy; override;
   property SubObject : TMySubObject read GetSubObject;
 end;
 
The TMyObjectEx extends TMyObject by including an object type property, using a sub class named "TMySubObject".

Again, we want to be able to read the MySubIntValue property value (integer). The MySubIntValue is a property of TMySubObject which is exposed as a read only property of the TMyObjectEx class.

Here's the implementation of the GetMySubIntValue getter:

 function TMyObjectEx.TMySubObject.GetMySubIntValue: integer;
 begin
   if Assigned(Self) then
     result := fMySubIntValue
   else
     result := -1; //default value for non assigned objects
 end;
 
 function TMyObjectEx.GetSubObject: TMySubObject;
 begin
   if Assigned(Self) then
   begin
     if fSubObject = nil then fSubObject := TMyObjectEx.TMySubObject.Create;
       result := fSubObject;
   end
   else
     result := nil;
 end;
 
 destructor TMyObjectEx.Destroy;
 begin
   FreeAndNil(fSubObject);
   inherited;
 end;
 
With the above declaration, you can ask for the value of "my.SubObject.MySubIntValue" even if "my" is NIL!

Therefore, the next line will not raise an access violation EVEN if "my" is NIL!

 Caption := my.StringProp + IntToStr(my.SubObject.MySubIntValue); 
Im not sure if it is "OOP-OK" to use the presented way of reading properties for non assigned objects - but I do use it and I'm happy not to always need to check if some object is assigned or not.

Of course, in some scenarioos you MUST check if an object is nil, but for some, the above idea would come handy :)

©2014 About.com. All rights reserved.