Delphi Win32 & Lambda Expressions

// 16 Mayıs 2009 // Delphi, Programlama

C# 3.0 ile birlikte gelen Lambda Expressionlar hakkında hemen hemen pek çok yerde makale yada yazıya rastlamak mümkün. Bu makalelerin bazılarında Lambda ifadelerin geleceğin programlama özelliği olduğu yönünde abartılı görüşler olsa da yine de faydalı ve olması gereken özellikler olduğu kanaatindeyim bende. Ancak bazı Lambdacıların da bilmesi gerekir ki bu programlama alemine kesinlikle yeni girmiş bir unsur değildir. Bazı programlama dillerinde bu yetenek çok uzun zamanlardan beri mevcut. Ancak maalesef Delphi’de henüz yok. En azından Win32 versiyonunda yok, Delphi Prism’de var.

O zaman bende kolları sıvadım ve tam anlamı ile Lambda Expressions gibi olmasa , onun gücüne yakınlaşamasa da, yine de benzer bir yapıyı kodlamak istedim. Lambda ifadelerinin genel kullanım amacı SQL sorgularında olduğu gibi bir veri kümesini filtrelemek olarak düşünülebilir. Bizde Delphi’de yazacağımız bir liste sınıfına bu özelliği kazandırmaya çalışacağız örneğimizde. Hatalarımız, eksiklerimiz olursa şimdiden affola diyerek başlayalım kodlamaya.

  TOperator = (opEqual, opGreater, opSmaller, opDifferent, opLike);

  TMyList = class(TList)
  public
    function Where(const PropertyName, PropertyValue : Variant; Operator : TOperator = opEqual) : TMyList;
    function InheritsFrom(const aClass : TClass) : TMyList;
  end;


Yukarıdaki şablonumuzdaki fonksiyonların aynı sınıfı döndürmesi(TMyList) bizim ana noktamız olacak. Şimdi bu basit liste sınıfımızın içerisinde depolamak istediğimiz TPerson sınıfının şablonuna bir bakalım:

  TPerson = class(TPersistent)
  private
    fName : String;
    fAge  : Integer;
  public
    constructor Create(const AName : String; const AAge : Integer);
  published
    property Name : String read fName write fName;
    property Age  : Integer read fAge write fAge;
  end;

{ TPerson }

constructor TPerson.Create(const AName: String; const AAge: Integer);
begin
  inherited Create;

  fName := AName;
  fAge  := AAge;
end;

Görüldüğü üzere bu da son derece basit bir sınıf. Şimdi liste sınıfımıza ait can alıcı metodları yazmaya başlayalım;

function TMyList.InheritsFrom(const aClass: TClass): TMyList;
var
  iCounter : Integer;
  obj	      : TObject;
begin
  Result := TMyList.Create;

  for iCounter := 0 to Count - 1 do
  begin
    obj := TObject(Items[iCounter]);
    if obj <> nil then
    	if obj.InheritsFrom(aClass) then Result.Add(obj);
  end;
end;

function TMyList.Where(const PropertyName, PropertyValue: Variant; Operator : TOperator = opEqual): TMyList;
var
  iCounter : Integer;
  obj        : TObject;
  Info	      : PPropInfo;
  found    : Variant;
begin
  Result := TMyList.Create;

  for iCounter := 0 to Count - 1 do
  begin
    obj := TObject(Items[iCounter]);
    if obj <> nil then
    begin
      Info := GetPropInfo(obj, PropertyName); // obj nesnesinde PropertyName isimli bir property tanımlı mı ?
      if Info = nil then Continue;
      found := GetPropValue(obj, PropertyName); // tanımlı ise o property'nin variant türünden değerini al.

      case Operator of // karşılaştırmaları yap, uygun kayıtları dönüş listesine ekle.
      	opEqual 	: if found =	PropertyValue then Result.Add(obj);
        opGreater 	: if found >PropertyValue then Result.Add(obj);
        opSmaller 	: if found < PropertyValue then Result.Add(obj);
        opDifferent : if found <> PropertyValue then Result.Add(obj);
        opLike	: if VarIsStr(found) and VarIsStr(PropertyValue) then
        		    if Pos(
                        	     VarToStrDef(PropertyValue, '-'),
                                     VarToStrDef(found, '')
                                    ) > 0 then Result.Add(obj);

      end; // case
    end; // if obj <> nil then
  end; // for iCounter := 0 to Count - 1 do
end;

Bu iki yöntem örneğimizin can damarları. Şimdi yöntemlerimizi de tanımladığımıza göre test edebiliriz.

procedure ShowResults(const List : TMyList);
var
  iCounter : Integer;
  Person   : TPerson;
begin
  for iCounter := 0 to List.Count - 1 do
    if List.Items[iCounter] <> nil then
      if TObject(List.Items[iCounter]) is TPerson then
      begin
      	Person := TPerson(List.Items[iCounter]);
      	ShowMessage(TPerson(Person).Name + '/' + InttoStr(TPerson(Person).Age));
      end else ShowMessage(TObject(List.Items[iCounter]).ClassName);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ret,
  mList : TMyList;
begin
  mList := TMyList.Create;

  mList.Add(TPerson.Create('Tuğrul HELVACI'		, 34));
  mList.Add(TPerson.Create('Olcay DAĞLI'			, 29));
  mList.Add(TPerson.Create('Eşref BEKDEMİR'		, 36));
  mList.Add(TPerson.Create('Özkan DANACI'		, 24));
  mList.Add(TPerson.Create('Tuğrul TATLICI'		, 18));
  mList.Add(TPerson.Create('Tuncay BAKLAVACI'	, 14));

  mList.Add(TButton.Create(nil));
  mList.Add(TEdit.Create(nil));
  mList.Add(TListBox.Create(nil));

  mList.Add(TList.Create);     // Bir TComponent türevi değil
  mList.Add(TObject.Create); // Bir TComponent türevi değil

  ret := mList.Where('Name', 'Tu', opLike);
  if ret <> nil then
  begin
    ShowResults(ret); // Tuğrul HELVACI, Tuğrul TATLICI, Tuncay BAKLAVACI
    FreeAndNil(ret);
  end;

  ret := mList.Where('Name', 'Olcay DAĞLI');
  if ret <> nil then
  begin
    ShowResults(ret); // Olcay DAĞLI
    FreeAndNil(ret);
  end;

  ret := mList.Where('Name', 'Tu', opLike).
  		   Where('Age', 14, opGreater).
                   Where('Age', 30, opSmaller);

  if ret <> nil then
  begin
    ShowResults(ret); // Tuğrul TATLICI
    FreeAndNil(ret);
  end;

  ret := mList.InheritsFrom(TComponent);
  if ret <> nil then
  begin
    ShowResults(ret); // TButton, TEdit, TListBox
    FreeAndNil(ret);
  end;

  mList.Free;
end;

initialization
  RegisterClasses([TPerson]);
end.

Not: uses kısmına RTTI’ya erişmek adına TypInfo’yu eklemeyi unutmayın ve RTTI’nın sadece TPersistent’ten türemiş nesnelerin published alanlarını bulabileceğini de hatırınızdan lütfen çıkartmayın.

Sevgiler, saygılar..

“Delphi Win32 & Lambda Expressions” için 8 Yorum

  1. Özkan Danacı diyor ki:

    Evet güzel bir çalışma olmuş abi ellerine sağlık.
    Dataset oluşturup 20.000 kaydın üzerinde kayıt denedim çat diye geldi kayıtlar. Performansda süper.
    Büyük küçük harf duyarlı çalışıyor.

  2. Tuğrul HELVACI diyor ki:

    Vay be neler de yapıyormuş :)

  3. Ferruh Koroglu diyor ki:

    Tuğrul hocam,

    eline sağlık, Sadettin Bey’in web sitesindeki yorumun

    üzerinden linki takip ettim,

    fakat kuyu çok derinmiş :)

    Eline sağlık…

  4. Tuğrul HELVACI diyor ki:

    Faydalı olabiliyor isem ne mutlu bize üstad. Çok ilginç bir kaç makale daha yazacağım sırada epey makale var kafamda, ama pek vakit bulamıyorum.

  5. Ferruh Koroglu diyor ki:

    Tuğrul Hocam,

    1- pdf olarak indirme olanağımız var mı makaleleri ?

    2- Eline sağlık tekrar, böyle güzel bilgiler ancak böyle güzel beyinlerden çıkar :)

  6. Tuğrul HELVACI diyor ki:

    2- Estağfirullah.
    1- Açıkçası PDF meselesi hiç aklıma gelmedi. Esasen makalelerin indirilmesinin istenmesi de aklıma gelmemişti :) Niyetim insanların indir-kullan yapmaları değil de birşeyleri öğrenmelerine yardımcı olmak olduğu için indirme hususu aklıma gelmemişti. Şimdi bir de PDF’e aktarmakla uğraşırsam makale yazma hevesim kaçabilir, arzu edersen ilerleyen zamanlarda yaparız birşeyler. Benim de elimde yok çünkü , aklıma geldikçe yazı editörünü açıp yazıyorum.

  7. obsteel diyor ki:

    Merhaba,
    yazının konusu olan linq ve lambda ile ilgili dokuman ararken burayı buldum.
    bu konuyla ilgili bilgiler ve örnek kod için çok teşekkürler.
    ufak bir düzeltme yapmak istedim, sanırım kodda bir leak var,
    where komutu her çağrıldığında yeni bir liste oluşturuluyor fakat sadece en son oluşturulan liste hafızadan siliniyor.

  8. Tuğrul HELVACI diyor ki:

    Evet kodda leak olması gerçekten çok doğal. Çünkü ilgili sınıflar herhangi bir interface’i implemente etmiyorlar, olması gerekeken ise bir interface implementasyonu idi. Maksat sadece yeni ufuklar açmak ama yine de ilginize ve yorumunuza teşekkür ederim.

Yorum Yazın