IBasic Base Interface 


The interface "IBasic" is the very base interface for all other interfaces that I'm using anywhere. It's properties and methods (see also the IBasic Reference) are very important, because they're directly available in every interface you'll ever come across when using mad* packages.

type IBasic = interface ['{53F8CE42-2C8A-11D3-A52D-00005A180D69}'];

The following 2 methods check whether the interface "IID" is supported by the implementing object. "Supports" only does the check, while "GetInterface" also returns the specified interface, if it's supported.

function IBasic.Supports     (const IID: TGuid         ) : boolean;
function IBasic.GetInterface (const IID: TGuid; out obj) : boolean;

The method "SelfAsTObject" returns the object instance that implements the IBasic interface. Generally you can't do much with this TObject pointer, since the implementing classes are private to their units. So this method is mainly useful when writing your own IBasic descendents.

function IBasic.SelfAsTObject : TObject;

There are several situations where you will encounter invalid interface instances. Sometimes a valid object gets invalid, because whatever was represented by the object got deleted. But the main reason why you'll have to deal with invalid interface instances is that all the mad* functions which are to return an interface instance never return "nil", even when they fail. They also do not raise an exception. Instead they're returning an invalid interface instance. "Invalid" means here that the property "IsValid" is false. You can handle invalid objects in the same way as valid objects. You can call all methods, you can use all properties, but they'll all simply do nothing, if the object is invalid.

function IBasic.IsValid : boolean;

When calling methods you often get a direct boolean result, which tells you whether the call succeeded or not. But how can you check whether a property assignment has succeeded? The usual behaviour would be to raise an exception if an assignment did not succeed, but I don't like that behaviour very much. So my properties don't react this way. But how can you then know whether a property assignment was successful or not? You can right after the assignment ask the following method, it will tell you the result of the last action:

function IBasic.Success : boolean;

When an error occurs, my interfaces almost never raise an exception, instead you'll get a function result which is indicating the error, or you can check the IBasic.Success method. After you found out that an error occured, you can ask the "LastErrorNo" and "LastErrorStr" properties about the reason. You can also set error codes and strings yourself.

property  IBasic.LastErrorNo  : cardinal;
property  IBasic.LastErrorStr : string;
procedure IBasic.SetLastError (no: cardinal; str: string = '');

Each "IBasic" descendent has 2 multi purpose properties, namely "StrBuf" and "Data", one being a string and one a pointer. For the "Data" pointer you can also define a function which is called when the interface instance is freed. In this situation you should then free the Data pointer.

Both multi purpose properties are not used by any object internally. That means you can freely do with them whatever you like, just like with the well known "tag" property that exists in a lot of VCL objects.

property IBasic.StrBuf : string;
property IBasic.Data   : pointer;

  TDataDestroyProc   = procedure (var data: pointer);
  TDataDestroyProcOO = procedure (var data: pointer) of object;

procedure IBasic.SetData (data            : pointer;
                          dataDestroyProc : TDataDestroyProc  ); overload;
procedure IBasic.SetData (data            : pointer;
                          dataDestroyProc : TDataDestroyProcOO); overload;

The following properties only have a meaning if our object is an item of one or more ICustomBasicList descendents. Each of the following properties correspond exactly to one parent list.

The property "Index" tells you at which position in the parent's item list our object is stored in the moment. The properties "OldIndex" and "LastChange" are useful when the parent list fires a TIListChangeEvent. You can in such a situation then ask what exactly has changed and what value the index had before the change.

The properties "Selected", "Focused" and "Checked" are useful e.g. when a parent ICustomBasicList shows it's items in a TListView.

type TChangeType = (lctUnchanged, lctChanged, lctNew, lctDeleted);

property IBasic.Index          [const parent: ICustomBasicList] : integer;

property IBasic.OldIndex       [const parent: ICustomBasicList] : integer;
property IBasic.LastChangeType [const parent: ICustomBasicList] : TChangeType;

property IBasic.Selected       [const parent: ICustomBasicList] : boolean;
property IBasic.Focused        [const parent: ICustomBasicList] : boolean;
property IBasic.Checked        [const parent: ICustomBasicList] : TExtBool;

A little extension to the madTypes unit:

type TDABasic = array of IBasic;