Here is the second part of this interesting topic. As I'm still trying to redeem myself from my non popular first example, I'm sure this one will reach the expectations. For this example I'm trying to mimic the way LINQ works, using generics and delegates and I have adapted my solution using fluent Interfaces as well. This solution presents a IQueryList which contains a TList<T> which can be queried like we were using SQL. So, we can select certain values and apply a where clause to filter the final list. This example will give you a hint on how to correctly implement fluent interfaces and how to extend this functionality for your applications.
Delegates:
uses Generics.Collections, Generics.Defaults; type TProc<T> = procedure (n : T) of Object; TProcList<T> = procedure (n : TList<T>) of Object; TFunc<T> = reference to function() : T; TFuncParam<T, TResult> = reference to function(param : T) : TResult; TFuncList<T, TResult> = reference to function(n : TList<T>) : TResult; TFuncListSelect<T, TResult> = reference to function(n : TList<T>) : TList<T>;
IQueryList:
IQueryList<T, TResult> = interface
function Where(const param : TFuncParam<T, TResult>) : IQueryList<T, TResult>;
function OrderBy(const AComparer: IComparer<T>) : IQueryList<T, TResult>;
function Select(const param : TFuncListSelect<T, TResult>) : IQueryList<T, TResult>;
function FillList(const param : TFuncList<T, TResult>) : IQueryList<T, TResult>;
function Distinct() : IQueryList<T, TResult>;
function List() : TList<T>;
end;
TQueryList<T, TResult> = class(TInterfacedObject, IQueryList<T, TResult>)
private
FList : TList<T>;
protected
function Where(const param : TFuncParam<T, TResult>) : IQueryList<T, TResult>;
function FillList(const param : TFuncList<T, TResult>) : IQueryList<T, TResult>;
function Select(const param : TFuncListSelect<T, TResult>) : IQueryList<T, TResult>;
function Distinct() : IQueryList<T, TResult>;
function List() : TList<T>;
function OrderBy(const AComparer: IComparer<T>) : IQueryList<T, TResult>;
public
constructor Create();
destructor Destroy(); override;
class function New: IQueryList<T, TResult>;
end;
constructor TQueryList<T, TResult>.Create();
begin
FList := TList<T>.Create;
end;
destructor TQueryList<T, TResult>.Destroy;
begin
if Assigned(FList) then
FList.Free;
inherited;
end;
function TQueryList<T, TResult>.Distinct: IQueryList<T, TResult>;
var
list : TList<T>;
i : integer;
begin
list := TList<T>.Create();
for i := 0 to FList.Count-1 do
begin
if not list.Contains(FList[i]) then
list.Add(FList[i]);
end;
FList.Free;
FList := list;
result := Self;
end;
function TQueryList<T, TResult>.FillList(const param : TFuncList<T, TResult>): IQueryList<T, TResult>;
begin
param(FList);
Result := Self;
end;
function TQueryList<T, TResult>.List: TList<T>;
begin
result := FList;
end;
class function TQueryList<T, TResult>.New: IQueryList<T, TResult>;
begin
result := Create;
end;
function TQueryList<T, TResult>.OrderBy(const AComparer: IComparer<T>): IQueryList<T, TResult>;
begin
FList.Sort(AComparer);
result := Self;
end;
function TQueryList<T, TResult>.Select(const param: TFuncListSelect<T, TResult>): IQueryList<T, TResult>;
begin
FList := param(FList);
result := Self;
end;
function TQueryList<T, TResult>.Where(const param: TFuncParam<T, TResult>): IQueryList<T, TResult>;
var
list : TList<T>;
i: Integer;
Comparer: IEqualityComparer<TResult>;
begin
list := TList<T>.Create();
for i := 0 to FList.Count-1 do
begin
Comparer := TEqualityComparer<TResult>.Default;
if not Comparer.Equals(Default(TResult), param(FList[i])) then
list.Add(FList[i]);
end;
FList.Free;
FList := list;
result := Self;
end;
Example Implementation <Integer, Boolean>:
procedure DisplayList();
var
IqueryList : IQueryList<Integer, Boolean>;
item : integer;
begin
//Create the list and fill it up with random values
IqueryList := TQueryList<Integer, Boolean>
.New()
.FillList(function ( list : TList<Integer> ) : Boolean
var k : integer;
begin
for k := 0 to 100 do
list.Add(Random(100));
result := true;
end);
//Display filtered values
for item in IqueryList
.Select(function ( list : TList<Integer> ) : TList<Integer>
var
k : integer;
selectList : TList<Integer>;
begin
selectList := TList<Integer>.Create;
for k := 0 to list.Count-1 do
begin
if Abs(list.items[k]) > 0 then
selectList.Add(list.items[k]);
end;
list.Free;
result := selectList;
end)
.Where(function ( i : integer) : Boolean
begin
result := (i > 50);
end)
.Where(function ( i : integer) : Boolean
begin
result := (i < 75);
end)
.OrderBy(TComparer<integer>.Construct(
function (const L, R: integer): integer
begin
result := L - R; //Ascending
end
)).Distinct.List do
WriteLn(IntToStr(item));
end;
This example fills up an initial list with 100 random numbers and then I query the list to give me all the values from the list which absolute value is greater than 0 and the values are between 50 and 75. From this list I want all the values ordered by value and I do not want repeated numbers (distinct method).
Example Implementation <String, String>:
procedure DisplayStrings();
const
Chars = '1234567890ABCDEFGHJKLMNPQRSTUVWXYZ!';
var
S: string;
IqueryList : IQueryList<String, String>;
item : String;
begin
//Fill up the list with random strings
IqueryList := TQueryList<String, String>
.New
.FillList(function ( list : TList<String> ) : String
var
k : integer;
l : Integer;
begin
Randomize;
for k := 0 to 100 do
begin
S := '';
for l := 1 to 8 do
S := S + Chars[(Random(Length(Chars)) + 1)];
list.Add(S);
end;
result := '';
end);
//Query the list and retrieve all items which contains 'A'
for item in IqueryList.Where(function ( i : string) : string
begin
if AnsiPos('A', i) > 0 then
result := i;
end).List do
WriteLn(item);
end;
The string example is quite similar, it fills up a list with 100 random string values and then it filters the list displaying only items which contains the character "A".
Everything is based on interfaces so the garbage collector can step in and avoid memory leaks and you can find a sound widespread of generics, delegates and chaining methods all in once. This solution gives you control on the way data is treated and it can work with any type as it is using generics.
I have included all those examples in my personal repository on Google project. Please feel free to use it and comment everything you like/dislike so we all can improve those examples. The Unit testing side includes interesting examples:
Enjoy it!.
I look forward to your comments.
I have included all those examples in my personal repository on Google project. Please feel free to use it and comment everything you like/dislike so we all can improve those examples. The Unit testing side includes interesting examples:
Enjoy it!.
I look forward to your comments.
Jordi
Related Links:





















