System.Object.ReferenceEquals

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

Delphi tanımı:

class function ReferenceEquals (objA : System.Object; objB : System.Object) : Boolean;

C# tanımı :

public static bool ReferenceEquals (Object objA, Object objB);

Her iki sınıfa ait nesnenin aynı referansa sahip olup olmadığına yada her ikisininde nil (Null) olup olmadığına bakar.Eğer her iki nesnede aynı referansa sahip ise yada her ikisi de nil(Null) ise true aksi taktirde false döndürür. Şimdi Referans diyoruz da nedir bu referans ? Aslında buraya C++ için yapılmış olan tanımıda yazmak istiyorum müsaadenizle.

public: static bool ReferenceEquals(
Object* objA,
Object* objB
);

Bu tanımda her iki paremetrenin tipide Object* biçiminde.Yani bu paremetreler aslında Object tipini işaret eden işaretçiler(Pointer). Aslında Delphi de tanımladığımız pek çok şeyin işaretçi olması gibi bir gerçek bu.

procedure TForm1.Button1Click(Sender: TObject);
var
  btn : TButton;
begin
  btn := TButton.Create(Self);
end;

Yukarıdaki basit kod örneğinde TButton türünde bir değişken tanımladık ve btn := TButton.Create(Self); satırı ile bir nesneye işaret eden bir işaretçimiz(pointer) oldu.

Bu kodu biraz daha genişletelim:

procedure TForm1.Button1Click(Sender: TObject);
var
	btn1 : TButton;
	btn2 : TButton;
	btn3 : TButton;
begin
	btn1 := TButton.Create(Self);
	btn1.Parent := Self;
	btn1.Left := 50;
	btn1.Top := 50;
	btn1.Width := 100;
	btn1.Height := 100;

	btn2 := TButton.Create(Self);
	btn2.Parent := Self;
	btn2.Left := 250;
	btn2.Top := 250;
	btn2.Width := 100;
	btn2.Height := 100;

	btn3 := btn2;
	btn3.Caption := 'Deneme';
end;

Yukarıdaki örnekte TButton sınıfından 3 adet değişkenimiz var. Bunların ikisi için hafızada yer ayırma işlemi yapılıyor(Create). Ama üçüncüsü için hafızada yer ayrılma işlemi yapılmıyor.Buna rağmen form üzerinde görünen btn2 nesnemizin başlık bilgisinin “Deneme” olarak değiştiğini gözlemleyeceğiz.Bunun sebebi ne olabilir.? Aslında olayın işleyişi şu şekilde gerçekleşiyor:

Delphi TButton.Create(Self) çağrısı ile bir button oluşturulmak istendiğini anlıyor ve hafızada button için belli bir yer ayırıyor.Ayırdığı yerin ilk byte’ını yani hafıza bloğunun adresini bize de geri döndürüyor.Bu durumda bizim btn1 değişkenimiz hafızada ayrılmış olan bloğa işaret eden bir gösterici(Pointer) oluveriyor. btn2 değişkeni içinde bu durum aynen geçerli. Ancak btn3 değişkeni için durum biraz farklı. Diyelimki btn2 değişkeni hafızadaki $101112 numaralı göze işaret eden bir işaretçi.Bu durumda aslında btn2 değişkeninin içerisinde bu adresten başka birşey yok ve btn3 := btn2 gibi bir atama ile btn3 de aynı hafıza gözünü gösteren bir başka işaretçi oluyor. Yani her ikisinin de içerikleri $101112 oluyor. Hafızanın bu gözünde ise bir adet TButton sınıfına ait nesne bulunuyor.Bu durumda btn2 ve btn3 işaretçileri(Pointer) aynı nesneyi işaret etmiş oluyorlar. Şimdi diyeceksiniz ki sende amma dolaştırdın lafı..Eh ReferenceEquals’ı anlatmak için aklıma başka anlamlı bir senaryo gelmedi. ;) Şimdi örneğimizi biraz daha genişletelim:

procedure TForm1.Button1Click(Sender: TObject);
var
  btn1 : TButton;
  btn2 : TButton;
  btn3 : TButton;
begin
  btn1 := TButton.Create(Self);
  btn1.Parent := Self;
  btn1.Left := 50;
  btn1.Top := 50;
  btn1.Width := 100;
  btn1.Height := 100;

  btn2 := TButton.Create(Self);
  btn2.Parent := Self;
  btn2.Left := 250;
  btn2.Top := 250;
  btn2.Width := 100;
  btn2.Height := 100;

  btn3 := btn2;
  btn3.Caption := 'Deneme';

  If System.Object.ReferenceEquals(btn1, btn2)
  then ShowMessage('İki değişkende aynı nesneye işaret ediyor')
  else ShowMessage('İki değişken farklı nesnelere işaret ediyor');

  If System.Object.ReferenceEquals(btn2, btn3)
  then ShowMessage('İki değişkende aynı nesneye işaret ediyor')
  else ShowMessage('İki değişken farklı nesnelere işaret ediyor');
end;

Yukarıdaki ReferenceEquals çağrılarından ilkinin sonucunda ekranda iki değişkeninde farklı nesnelere işaret ettiği mesajını , ikincisinde ise aynı nesneye işaret ettiği mesajını alırsınız. ReferenceEquals bu işe yarıyor. Şimdi gelin .Net ortamında ReferenceEquals yöntemini kullanmadan karşılaştıralım.O zaman kodu biraz daha genişleteceğiz ;)

{$UNSAFECODE ON}
procedure TForm1.Button1Click(Sender: TObject); unsafe;
var
  btn1 : TButton;
  btn2 : TButton;
  btn3 : TButton;

  pBirinciNesne,
  pIkinciNesne : ^Integer;
begin
  btn1 := TButton.Create(Self);
  btn1.Parent := Self;
  btn1.Left := 50;
  btn1.Top := 50;
  btn1.Width := 100;
  btn1.Height := 100;

  btn2 := TButton.Create(Self);
  btn2.Parent := Self;
  btn2.Left := 250;
  btn2.Top := 250;
  btn2.Width := 100;
  btn2.Height := 100;

  btn3 := btn2;
  btn3.Caption := 'Deneme';

  If System.Object.ReferenceEquals(btn1, btn2)
  then ShowMessage('İki değişkende aynı nesneye işaret ediyor')
  else ShowMessage('İki değişken farklı nesnelere işaret ediyor');

  If System.Object.ReferenceEquals(btn2, btn3)
  then ShowMessage('İki değişkende aynı nesneye işaret ediyor')
  else ShowMessage('İki değişken farklı nesnelere işaret ediyor');

  pBirinciNesne := @btn1;
  pIkinciNesne := @btn2;

  if pBirinciNesne^ = pIkinciNesne^ // İki pointer’ın içeriğini kıyasla
  then ShowMessage('İki değişkende aynı nesneye işaret ediyor')
  else ShowMessage('İki değişken farklı nesnelere işaret ediyor');

  pBirinciNesne := @btn2;
  pIkinciNesne := @btn3;

  if pBirinciNesne^ = pIkinciNesne^ // İki pointer’ın içeriğini kıyasla
  then ShowMessage('İki değişkende aynı nesneye işaret ediyor')
  else ShowMessage('İki değişken farklı nesnelere işaret ediyor');
end;

Hımm burada biraz değişik bir durum göze çarpıyor.Unsafe diye bir şey gelmiş procedure’nin sonuna.Acep o da ne oluyor..? Şimdi unsafe koymadan programı derlemeye çalıştığınızda Delphi size güvensiz(unsafe) kodun sadece unsafe olarak işaretlenmiş procedure’lerde kullanılabileceğini belirten bir hata mesajı veriyor.Eh tamam Delphi abi kızmasın diyoruz hemen procedure’nin sonuna unsafe; koyuveriyoruz.Ama o da ne Delphi hala kızıyor.Tersinden kalkmış galiba bugün.

Neyse bakıyoruz hata mesajına, Delphi bize ; “hemşerim madem o kadar güvenli kod varken illa da güvensiz yazacağım ben diyorsun o zaman tepelere biryerlere {$UNSAFECODE ON} yaz da ben de anlayayım güvensiz kod çalıştırılacağını.Ben de ona göre CLR’a bildireceğim hayret birşeysin, bir daha olmasın ona göre !” diyor.Bizde tamam kızma abi diyip yazıyoruz tepeye bir yere.O zaman Delphi kodu derlemeye yanaşıyor.Eee şimdi bu unsafe olayı da neyin nesi, nereden çıktı diyebilirsiniz.Bende şu anda derinlemesine bilmiyor olsam da , pointersal işlemlerin yada API çağrılarının yapılması yönetimli bir ortam olan CLR’ın elinden yönetimi almak anlamına geliyor. Yani CLR size kısaca ne halin varsa gör diyor. Muhtemelen bizim bu Unsafe kod bloğunda Button’ları Create ettiğimiz ama Free etmediğimiz için ve CLR’da bu bloğu yönetmeyeceği için çöp toplama mekanizması(GC) devreye girmeyip, bizim nesneler sonsuz bir yaşama doğru yelken açmış oluyorlar(Memory Leak). Ama elbette tam emin değilim, ilerleyen öğrenme süreçlerimde bu konulara da bulaşacağımdan ve size işkence etmeye devam edeceğimden emin olabilirsiniz. :)

Saygılar,sevgiler..

Yorum Yazın