Delphi, ilginçlikler diyarı..!

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

Delphi gerçekten de çok güçlü bir dildir..Ama onu hepimiz yeterince iyi kullanabiliyormuyuz işte orası tatışılır. Sizlerle birkaç ilginç kullanımı paylaşmak ve dilim döndüğünce anlatmak istiyorum.Mesela hemen hemen her Delphi programcısı herhangi bir nesnenin OnClick olay yöneticisinde belli başlı kodlar yazmıştır.Tüm OnClickler şöyle görünür.

  procedure …Click(Sender : TObject);

Peki düşündük mü acaba , “yahu bu Sender : TObject de nereden geliyor ?” diye.. Bu tanım aslında TNotifyEvent adlı bir tanımdan gelmektedir. Delphi Help’i açar ve TNotifyEvent’de neymiş diyip bakarsanız şöyle bir tanım göreceksiniz:

  type TNotifyEvent = procedure (Sender: TObject) of object;

Hımm bu da ne şimdi ? TNotifyEvent diye bir şey tanımlanmış ve ona sen bir proceduresin denmiş TObject türünde de bir parametren var denilmiş. Ehh biraz anladık, iyi de o sondaki of object de ne oluyor ? Açıklayacağız meraklanmayın.Ancak ondan önce farkettiğimiz daha doğrusu aklımıza gelen bazı cinlikleri deneyelim.

var
  fMyProcedure : procedure (Sender : TObject);
 begin
 // bilimum kodlar..
 end;

Hımm ilginç yukarıdaki tanımlamaya Delphi kızmadı.Peki bu ne işe yarar ki diye düşünürken acaba bir function’a yada bir procedure’ye bir başka function yada procedure’yi parametre olarak geçebilir miyiz ? Evet, gayet ilginç bir düşünce.Hemen bir deneme yapalım.

function Test : String;
 begin
   Result := ‘Deneme yapıyoruz.!’;
 end;

 procedure TForm1.functionDeneme( function : String );
 begin
  // …….
 end;

Yukarıda Test adlı bir function tanımladık ve daha sonra functionDeneme metodunda String döndüren bir parametreyi procedure’mize eklemeye çalıştık ama olmadı.! Delphi hata verdi. Eee o zaman nasıl yapacağız ki ? Acaba şöyle yapsak olur mu ?

function Test : String;
 begin
   Result := ‘Deneme yapıyoruz.!’;
 end;

 type TFunctionParam = function : String;

 procedure functionDeneme( funcParam : TFunctionParam );
 begin
  // …….
 end;

Evet..! Şimdi oldu Delphi kızmadı. Peki bunu nasıl kullanacağız.Örneklerle onu inceleyelim..

type TFunctionParam = function : String;

function Test1 : String;
begin
  Result := ‘Test1 fonksiyonu..!’;
end;

function Test2 : String;
begin
  Result := ‘Test2 fonksiyonu..!’;
end;

function Test3 : String;
begin
  Result := ‘Test3 fonksiyonu..!’;
end;

procedure functionDeneme( funcParam : TFunctionParam );
var
  sResult : String;
begin
  sResult := funcParam;
  ShowMessage(sResult);
end;

procedure TForm1.Button1Click(Sender : TObject);
var
  fMyFunction : TFunctionParam;
begin
  fMyFunction := Test1;
  functionDeneme(fMyFunction);

  fMyFunction := Test2;
  functionDeneme(fMyFunction);

  fMyFunction := Test3;
  functionDeneme(fMyFunction);
end;

Evet ilginç bir kullanım..Bir procedure’nin içerisine bir fonksiyonu parametre olarak geçtik ve beklediğimiz gibi test mesajlarını ekranda gördük.Parametreye sahip olan bir procedure yada function geçmek için ise ne yapılması gerektiği maalesef şu an aklıma gelmiyor bir türlü, ancak hatırlayabilirsem sizinle paylaşacağıma emin olabilirsiniz. Değişik örneklere devam edelim.

type TIntegerFunction = function(const Value : Integer) : Integer;

function NextResult(const Value : Integer) : Integer;
begin
  Result := Value + 1;
end;

function PreviousResult(const Value : Integer) : Integer;
begin
  Result := Value - 1;
end;

function SqrResult(const Value : Integer) : Integer;
begin
  Result := Value * Value;
end;

var
 fFunc : TIntegerFunction;
 iValue : Integer;
begin
  fFunc  := NextResult;
  iValue := fFunc(10); // iValue = 11

  fFunc  := PreviousResult;
  iValue := fFunc(10); // iValue = 9

  fFunc  := SqrResult;
  iValue := fFunc(10); // iValue = 100
end;

Yukarıdaki tanımlama ile aynı etkiyi yapacak kod ise şu şekilde yazılabilir.

function NextResult(const Value : Integer) : Integer;
begin
  Result := Value + 1;
end;

function PreviousResult(const Value : Integer) : Integer;
begin
  Result := Value - 1;
end;

function SqrResult(const Value : Integer) : Integer;
begin
  Result := Value * Value;
end;

var
 fFunc  : function(const Value : Integer) : Integer;
 iValue : Integer;
begin
  fFunc  := NextResult;
  iValue := fFunc(10); // iValue = 11

  fFunc  := PreviousResult;
  iValue := fFunc(10); // iValue = 9

  fFunc  := SqrResult;
  iValue := fFunc(10); // iValue = 100
end;

Görüldüğü üzere tek fark bir type bloğu ile tip olarak tanımlanmamış olmasıdır.Peki bu atamaları run-time sırasında belli bir koşula göre yapsak ve değişkenimizin istediğimiz bir anda hangi fonksiyonu gösterdiğini nasıl bulabiliriz ?

function NextResult(const Value : Integer) : Integer;
begin
  Result := Value + 1;
end;

function PreviousResult(const Value : Integer) : Integer;
begin
  Result := Value - 1;
end;

function SqrResult(const Value : Integer) : Integer;
begin
  Result := Value * Value;
end;

var
 fFunc  : function(const Value : Integer) : Integer;
 iValue : Integer;
begin
  fFunc  := NextResult;
  iValue := fFunc(10); // iValue = 11

  if @fFunc = @NextResult then ShowMessage(‘NextResult’) else ShowMessage(‘Boş yada bilinmiyor’);

  fFunc  := PreviousResult;
  iValue := fFunc(10); // iValue = 9

  if @fFunc = @PreviousResult then ShowMessage(‘PreviousResult’) else ShowMessage(‘Boş yada bilinmiyor’);

  fFunc  := SqrResult;
  iValue := fFunc(10); // iValue = 100
  if @fFunc = @SqrResult then ShowMessage(‘SqrResult’) else ShowMessage(‘Boş yada bilinmiyor’);
 
  fFunc := nil;
  if @fFunc = @SqrResult then ShowMessage(‘SqrResult’) else ShowMessage(‘Boş yada bilinmiyor’);
end;

Yukarıdaki örnekte en can alıcı nokta her iki fonksiyonun da adreslerinin karşılaştırılıyor olmasıdır.Çünkü Delphi fFunc := NextResult; gibi bir atamada fFunc değişkenine NextResult fonksiyonunun adresini atamaktadır.Görüldüğü üzere Delphi’de Pointer’lar fazlasıyla kullanılmaktadır. Evet kısaca buraya kadar neleri yapabilip neleri yapamadığımızı kısaca özetlemek gerekir ise;

var bloğunda tıpkı değişken tanımlar gibi istediğiniz kadar parametreye sahip bir procedure yada function tanımlayabilir ve tanımladığınız bu değişkene aynı parametre sayısına sahip procedure yada functionu’nuzu eşitleyebilirsiniz.

Herhangi bir procedure yada function’a parametre olarak bir başka procedure yada function’u geçmek istiyorsanız o zaman type yapısını kullanmalı ve yeni bir tip oluşturmalısınız.

Procedure yada function eşitlenmiş olan bir değişkenin içeriğini kontrol etmek için adres operatöründen (@) yararlanabilirsiniz.

Peki şimdi en baştaki TNotifyEvent tanımını tekrar gözden geçirelim.Nasıldı tanım:

  type TNotifyEvent = procedure (Sender: TObject) of object;

Peki buraya kadar herşeyi anladık da bu of object de neyin nesi oluyor ? Onu da şu şekilde açıklayalım. Eğer tanımlamış olduğunuz bir değişkene bir sınıfın içerisinde tanımlanmış olan bir procedure yada function’u eşitlemek isterseniz o zaman tip tanımınız TNotifyEvent’te olduğu gibi of object tanımı ile bitmelidir.Yok bizim şu ana kadar yaptığımız gibi herhangi bir sınıfa üye olmayan kendi başına bulunan bir function yada procedure eşitlemesi yapacaksanız o zaman of object kullanmanıza gerek yok.Şimdi bunu kodla kısaca göstermeye çalışalım:

TMyFunction = function(const Value : Integer) : Integer;

 TMyClass = class(TComponent)
 public
   function MyFunc(const Value : Integer) : Integer;
 end;

 function TMyClass.MyFunc(const Value : Integer) : Integer;
 begin
    Result := Value + 1;
 end;

 var
   fFunc : TMyFunction;
   mClass: TMyClass;
   iValue : Integer;
 begin
   mClass := TMyClass.Create(nil);
   fFunc := mClass.MyFunc;
   iValue := fFunc(100);
 end;

Yukarıdaki gibi bir kodda Delphi kızacak ve bize şöyle bağıracaktır. “Incompatible types regular procedure and method pointer”..Hımm peki o zaman

TMyFunction = function(const Value : Integer) : Integer; tanımımızı

TMyFunction = function(const Value : Integer) : Integer of object;

olarak değiştirirsek Delphi’nin bizden şikayetçi olmayı bıraktığını göreceğiz.Bunun nedeni ise gayet basittir. Bir sınıf tanımının içerisindeki bir function yada procedure’ye hafızada belli bir yer ayrılması ancak o sınıf create edildikten sonra yani nesne olduğu zaman gerçekleşir.Bir nesneye ait olan bir function ve procedure’ye ise ancak bir nesne üzerinden erişilebilir.İşte bu yüzden tanımımızın sonuna of object ekliyoruz.

Hazır var bloğu ile uğraşırken bu blokta isimsiz record da tanımlayabileceğinizi belirtmek isterim.Şöyle ki;

var
  mRecord : record
      Adi : String;
      Soyadi : String;
      Yasi : String;
  end;
begin
  mRecord.Adi := ‘Adınız’;
  mRecord.Soyadi := ‘Soyadınız’;
  mRecord.Yasi := 25;

  ShowMessage(
                      mRecord.Adi + ‘ - ‘ +
                      mRecord.Soyadi + ‘ - ‘ +
                      IntToStr(mRecord.Yasi)
                    );
end;

Delphi ilginçlikler diyarında kısa gezintimizin şimdilik sonuna geldik.

Saygılar, sevgiler..

Yorum Yazın