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..