Can I use generics to do the same operation on similar types of controls?
I am using Delphi 2010 and I have a unit where over the years I have added my own procedures and functions that can be used with any project I make, such as:
function ListBoxIsSelected(ListBox: TListBox): Boolean;
begin
Result:= ListBox.ItemIndex <> -1;
end;
The above uses TListBox
as a parameter, so whenever the above function is used I must supply a listbox
that is of TListBox
class.
Now suppose I have some other component librar开发者_开发问答ies that could work with the same function, For example the Jedi component classes.
How could I use the above function, when the Jedi listbox
is TJvListBox
class and my function is looking for TListBox
class? Although both components are practically the same, the class names are different. If I provided the same function specifically for the TJvListBox
it would likely work because they are both "listboxes":
function ListBoxIsSelected(ListBox: TJvListBox): Boolean;
begin
Result:= ListBox.ItemIndex <> -1;
end;
Now, I have whole load of procedures and functions written in the same kind of way where I need to pass a component as a parameter. Having to rewrite them again just to work with a different component class is not feasible!
How can I write this with generics?
You can't write that with generics, unless your target classes all descend from the same base class of course. (But then you wouldn't need generics for it.)
If you really want something that can check if the ItemIndex property on any object <> -1, though, you can do that with a different Delphi 2010 feature: extended RTTI.
uses
SysUtils, RTTI;
function IsSelected(item: TObject): boolean;
var
context: TRttiContext;
cls: TRttiType;
prop: TRttiProperty;
ItemIndex: integer;
begin
if item = nil then
raise Exception.Create('Item = nil');
context := TRttiContext.Create;
cls := context.GetType(item.ClassType);
prop := cls.GetProperty('ItemIndex');
if prop = nil then
raise Exception.Create('Item does not contain an ItemIndex property.');
ItemIndex := prop.GetValue(item).AsInteger;
result := ItemIndex <> -1;
end;
Careful, though. There's no compile-time type checking here, and this process is significantly slower than your original routine. You probably won't notice it, but if you call something like this in a tight loop, it will slow it down.
I don't understand how I can write this with Generics?
You can’t – not unless your component implements a common interface or inherits from a common base class with the standard ListBox
, and that interface / base class offers the ItemIndex
property.
In fact, this use-case isn’t such a great example of generics because using an interface or base class in the declaration would work just as well.
In this case, you can write two overloaded functions, one expecting TJvListBox and the other expecting TListBox.
In more complex cases this approach may not apply so well, but I think your case is simple enough for this solution.
I cannot look it up right now (on holiday, no Delphi), but don't TJvListBox and TListBox descend from a common ancestor (my guess would be: TCustomListBox)? In that case something like this should work:
interface
function TListBox_IsItemSelected(_ListBox: TCustomListBox): boolean;
implementation
function TListBox_IsItemSelected(_ListBox: TCustomListBox): boolean;
begin
Result := _ListBox.ItemIndex <> -1;
end;
Just in case ItemIndex (as I said: I cannot check right now) is protected in TCustomListBox, you can just use a typecast hack:
type
TListBoxHack = class(TCustomListBox)
end;
function TListBox_IsItemSelected(_ListBox: TCustomListBox): boolean;
begin
Result := TListBoxHack(_ListBox).ItemIndex <> -1;
end;
(I just thought I should mention this since the original question has already been answered: Using Generics does not help here.)
精彩评论