8. Kullanıcı olayları
Bir önceki bölümde, form bileşenleriyle ilgili olay kavramını ele almıştık. Şimdi ise kendi sınıflarımızda nasıl olaylar oluşturacağımızı göreceğiz.
8.1. Önceden tanımlanmış delege nesneleri
delegate nesnesi kavramı önceki bölümde ele alınmıştı, ancak o zaman sadece üstünkörü geçilmişti. Bir formun bileşenlerinin olay işleyicilerinin nasıl tanımlandığını incelediğimizde, aşağıdakine benzer bir kodla karşılaşmıştık:
this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
burada buttonAfficher, [Button] türünde bir bileşendi. Bu sınıfta, Click alanı şu şekilde tanımlanmıştır:
![]() |
- [1]: [Button] sınıfı
- [2]: olayları
- [3,4]: Click olayı
- [5]: [Control.Click] [4] olayının bildirimi.
- EventHandler, delegate olarak adlandırılan bir yöntem prototipidir (modeldir).
- event, delegate ve EventHandler'in işlevlerini kısıtlayan bir anahtar kelimedir: bir delegate nesnesi, bir event nesnesinden daha zengin işlevlere sahiptir.
delegate ve EventHandler şu şekilde tanımlanmıştır:
![]() |
delegate ve EventHandler, bir yöntem şablonunu ifade eder:
- 1. parametresi Object türü olan
- 2. parametresi EventArgs türündedir
- hiçbir sonuç döndürmez
EventHandler ile tanımlanan modele karşılık gelen bir yöntem şu şekilde olabilir:
private void buttonAfficher_Click(object sender, EventArgs e);
EventHandler türünde bir nesne oluşturmak için aşağıdaki adımlar izlenir:
EventHandler evtHandler=new EventHandler(méthode correspondant au prototype défini par le type EventHandler);
Böylece şu şekilde yazılabilir:
delegate türündeki bir değişken, aslında delegate modeline karşılık gelen yöntemlere ilişkin bir referans listesidir. Yukarıdaki evtHandler değişkenine yeni bir M yöntemi eklemek için şu sözdizimi kullanılır:
evtHandler boş bir liste olsa bile += notasyonu kullanılabilir.
Aşağıdaki komut:
this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
buttonAfficher.Click olayının yöntem listesine EventHandler türünde bir yöntem ekler. buttonAfficher bileşeninde Click olayı meydana geldiğinde, VB şu komutu yürütür:
burada:
- source, object türündeki ve EventArgs türündeki source olayını tetikleyen bileşendir
- evt'i tetikleyen bileşendir ve bilgi içermez
Click olayına şu şekilde ilişkilendirilmiş tüm void M(object,EventArgs) imza yöntemleri:
this.buttonAfficher.Click += new System.EventHandler(M);
VB tarafından iletilen (source, evt) parametreleriyle çağrılacaktır.
8.2. Delegate nesnelerini tanımlama
Şu komut
public delegate int Opération(int n1, int n2);
komutu, iki tamsayı alan ve bir tamsayı döndüren bir işlev prototipini temsil eden Opération adlı bir türü tanımlar. Opération'i bir işlev prototipi tanımı haline getiren, delegate anahtar sözcüğüdür.
Opération türündeki bir op değişkeni, Opération prototipine karşılık gelen işlevlerin listesini kaydetme görevini üstlenecektir:
fi yönteminin op değişkenine kaydedilmesi şu şekilde yapılır: op=new Opération(fi) komutuyla veya daha basitçe op=fi. komutuyla gerçekleştirilir. Zaten kaydedilmiş fonksiyonlar listesine bir fj yöntemi eklemek için op+= fj yazılır. Zaten kaydedilmiş bir fk yöntemini kaldırmak için op-=fk yazılır. Örneğimizde n=op(n1,n2) yazılırsa, op değişkeninde kayıtlı tüm yöntemler, n1 ve n2 parametreleriyle çalıştırılacaktır. Elde edilen n sonucu, en son yürütülen yöntemin sonucudur. Tüm yöntemlerin ürettiği sonuçları elde etmek mümkün değildir. Bu nedenle, bir delege fonksiyona bir yöntem listesi kaydedilirse, bu yöntemler çoğunlukla void türünde bir sonuç döndürür.
Aşağıdaki örneği ele alalım:
using System;
namespace Chap6 {
class Class1 {
// fonksiyon prototipi tanımlama
// 2 tamsayı parametre alır ve bir tamsayı döndürür
public delegate int Opération(int n1, int n2);
// prototipe karşılık gelen iki örnekleme yöntemi
public int Ajouter(int n1, int n2) {
Console.WriteLine("Ajouter(" + n1 + "," + n2 + ")");
return n1 + n2;
}//ekle
public int Soustraire(int n1, int n2) {
Console.WriteLine("Soustraire(" + n1 + "," + n2 + ")");
return n1 - n2;
}// çıkarma
// prototipe karşılık gelen bir statik yöntem
public static int Augmenter(int n1, int n2) {
Console.WriteLine("Augmenter(" + n1 + "," + n2 + ")");
return n1 + 2 * n2;
}//artır
static void Main(string[] args) {
// işlem türü bir nesne tanımlayarak fonksiyonları bu nesneye kaydediyoruz
// "artır" statik fonksiyonu kaydedilir
Opération op = Augmenter;
// delegeyi çalıştırıyoruz
int n = op(4, 7);
Console.WriteLine("n=" + n);
// class1 türünde bir c1 nesnesi oluşturulur
Class1 c1 = new Class1();
// c1 nesnesinin "ekle" yöntemini delegeye kaydedilir
op = c1.Ajouter;
// delege nesnesinin çalıştırılması
n = op(2, 3);
Console.WriteLine("n=" + n);
// c1'in "çıkarma" yöntemini delegeye kaydediyoruz
op = c1.Soustraire;
n = op(2, 3);
Console.WriteLine("n=" + n);
//Delegeye iki fonksiyonun kaydedilmesi
op = c1.Ajouter;
op += c1.Soustraire;
// delege nesnesinin çalıştırılması
op(0, 0);
// delege nesnesinden bir fonksiyonun kaldırılması
op -= c1.Soustraire;
// delege çalıştırılır
op(1, 1);
}
}
}
- 3. satır: Class1 sınıfını tanımlar.
- 6. satır: delegate'in tanımı Opération: int türünde iki parametre alan ve int türünde bir sonuç döndüren yöntem prototipi
- 9-12. satırlar: Ajouter örnek yöntemi, Opération delege imzasına sahiptir.
- satır 14-17: Soustraire örnek yöntemi, Opération delege imzasına sahiptir.
- 20-23. satırlar: Augmenter sınıf yöntemi, Opération delege imzasına sahiptir.
- 25. satır: Main yöntemi yürütülür
- 20. satır: op değişkeni, Opération delegat türündedir. Bu değişken, Opération delegat türünün imza yapısına sahip yöntemlerin bir listesini içerecektir. Bu değişkene, statik Class1.Augmenter yöntemine ait ilk yöntem referansı atanır.
- 31. satır: delegate op yürütülür: op tarafından referans verilen tüm yöntemler yürütülecektir. Bu yöntemler, delegate ve op.'e aktarılan parametrelerle çalıştırılacaktır. Burada, yalnızca Class1.Augmenter statik yöntemi çalıştırılacaktır.
- 35. satır: Class1 sınıfından bir c1 örneği oluşturulur.
- 37. satır: c1.Ajouter örnek yöntemi, op delegatına atanır. Augmenter statik bir yöntemken, Ajouter bir örnek yöntemdir. Bunun bir önemi olmadığını göstermek istedik.
- 39. satır: delegate op yürütülür: Ajouter yöntemi, delegate op'e aktarılan parametrelerle yürütülecektir.
- 42. satır: Aynı işlem, Soustraire örnek yöntemi için de tekrarlanır.
- 46-47. satırlar: Ajouter ve Soustraire yöntemleri, delegate ve op yöntemlerine eklenir.
- 49. satır: delegate ve op yürütülür: Ajouter ve Soustraire yöntemlerinin her ikisi de, delegate ve op'e aktarılan parametrelerle çalıştırılacaktır.
- 51. satır: Soustraire yöntemi, delegate ve op'ten kaldırılır.
- 53. satır: delegate ve op yürütülür: kalan Ajouter yöntemi de yürütülecektir.
Yürütme sonuçları şunlardır:
8.3. Delege mi, yoksa arayüz mü?
delegates ve arayüz kavramları birbirine oldukça yakın görünebilir ve bu iki kavram arasındaki farkların tam olarak ne olduğu merak edilebilir. Daha önce incelediğimiz bir örneğe benzer şu örneği ele alalım:
using System;
namespace Chap6 {
class Program1 {
// bir işlev prototipinin tanımlanması
// 2 tamsayı parametre alır ve bir tamsayı döndürür
public delegate int Opération(int n1, int n2);
// prototipe karşılık gelen iki örnek yöntemi
public static int Ajouter(int n1, int n2) {
Console.WriteLine("Ajouter(" + n1 + "," + n2 + ")");
return n1 + n2;
}//ekle
public static int Soustraire(int n1, int n2) {
Console.WriteLine("Soustraire(" + n1 + "," + n2 + ")");
return n1 - n2;
}// çıkarma
// Bir delege yürütülür
public static int Execute(Opération op, int n1, int n2){
return op(n1, n2);
}
static void Main(string[] args) {
// Ekle delegesinin yürütülmesi
Console.WriteLine(Execute(Ajouter, 2, 3));
// delege yürütme Çıkarma
Console.WriteLine(Execute(Soustraire, 2, 3));
// bir çoklu yayın delege işlevinin yürütülmesi
Opération op = Ajouter;
op += Soustraire;
Console.WriteLine(Execute(op, 2, 3));
// delege işlevinden bir işlev çıkarılır
op -= Soustraire;
// delege çalıştırılıyor
Console.WriteLine(Execute(op, 2, 3));
}
}
}
- satırda, Execute yöntemi, 6. satırda tanımlanan Opération delegat türünde bir nesneye referans beklemektedir. Bu, Execute yöntemine farklı yöntemlerin (26, 28, 32 ve 36. satırlar) aktarılmasını sağlar. Bu polimorfizm özelliği bir arayüz kullanılarak da elde edilebilir:
using System;
namespace Chap6 {
// IOperation arayüzü
public interface IOperation {
int operation(int n1, int n2);
}
// Add sınıfı
public class Ajouter : IOperation {
public int operation(int n1, int n2) {
Console.WriteLine("Ajouter(" + n1 + "," + n2 + ")");
return n1 + n2;
}
}
// Çıkarma sınıfı
public class Soustraire : IOperation {
public int operation(int n1, int n2) {
Console.WriteLine("Soustraire(" + n1 + "," + n2 + ")");
return n1 - n2;
}
}
// test sınıfı
public static class Program2 {
// IOperation arayüzünün tek yönteminin yürütülmesi
public static int Execute(IOperation op, int n1, int n2) {
return op.operation(n1, n2);
}
public static void Main() {
// Ekle delegesinin yürütülmesi
Console.WriteLine(Execute(new Ajouter(), 2, 3));
// Çıkarma delege yönteminin yürütülmesi
Console.WriteLine(Execute(new Soustraire(), 2, 3));
}
}
}
- 6-8. satırlar: [IOperation] arayüzü, operation yöntemini tanımlar.
- satır 11-16 ve 19-24: [Ajouter] ve [Soustraire] sınıfları, [IOperation] arayüzünü uygular.
- 29-31. satırlar: İlk parametresi IOperation arayüzünün türünde olan Execute yöntemi. Execute yöntemi, birinci parametre olarak sırasıyla Ajouter sınıfından bir örnek, ardından da Soustraire sınıfından bir örnek alacaktır.
Önceki örnekte delegate türündeki parametrede görülen polimorfik özellik burada da mevcuttur. Her iki örnek de aynı zamanda bu iki kavram arasındaki farkları göstermektedir.
delegate ve interface türleri,
- . Aslında, delegate türü tek bir yöntem için bir sarmalayıcıdır; oysa arayüz birden fazla yöntem tanımlayabilir.
- delegate'in çoklu yayın özelliği kullanılmıyorsa. Zaten arayüzde çoklu yayın kavramı mevcut değildir.
Bu iki koşul karşılanıyorsa, Execute yöntemi için aşağıdaki iki imza arasından seçim yapılabilir:
int Execute(IOperation op, int n1, int n2)
int Execute(Opération op, int n1, int n2)
delegate'i kullanan ikinci imza, kullanım açısından daha esnek olabilir. Zira ilk imzada, yöntemin ilk parametresi IOperation arayüzünü uygulamalıdır. Bu durum, Execute yöntemine ilk parametre olarak aktarılacak yöntemi tanımlamak üzere bir sınıf oluşturulmasını gerektirir. İkinci imza biçiminde ise, doğru imza biçimine sahip herhangi bir mevcut yöntem iş görür. Ek bir yapı oluşturmaya gerek yoktur.
8.4. Olay Yönetimi
delegate nesneleri, olayları tanımlamak için kullanılabilir. Bir C1 sınıfı, bir evt olayını şu şekilde tanımlayabilir:
- delegate türü, C1 sınıfının içinde veya dışında tanımlanır:
- C1 sınıfı, delegate Evt türünde bir alan tanımlar:
- C1 sınıfının bir c1 örneği bir olayı bildirmek istediğinde, Evt delegasyonu tarafından tanımlanan parametreleri ona aktararak Evt1 delegasyonunu çalıştıracaktır. delegate ve Evt1 sınıflarında kayıtlı tüm yöntemler bu parametrelerle çalıştırılacaktır. Bu yöntemlerin Evt1 olayından haberdar oldukları söylenebilir.
- c2 nesnesi, c1 nesnesini kullanarak c1 nesnesinde Evt1 olayının meydana geldiği konusunda bilgilendirilmek isterse, c1. nesnesinin c1.Evt1 adlı delege nesnesine c2.M adlı yöntemlerinden birini kaydedecektir. Böylece,c1 nesnesinde Evt1 olayı meydana geldiğinde bu yöntem çalıştırılacaktır. Ayrıca, olay hakkında artık bildirim almak istemediğinde kaydını silebilir.
- Tıpkı c1.Evt1 delege nesnesinin birden fazla yöntem kaydedebilmesi gibi, farklı ci nesneleri, c1 üzerindeki Evt1 olayından haberdar olmak için c1.Evt1 delegesine kaydolabilir.
Bu senaryoda şunlar vardır:
- bir olay bildiren bir sınıf
- bu olaydan haberdar edilen sınıflar. Bunların olaya abone oldukları veya olaya kayıt oldukları söylenir.
- olaydan haberdar edilecek yöntemlerin imzasını tanımlayan bir delegate türü
.NET çerçeve şunu tanımlar:
- delegate olayının standart imzası
- source: Olayı bildiren nesne
- evtInfo: olay hakkında bilgi sağlayan, EventArgs türü veya bunun türevlerinden bir nesne
- delegate adının sonu EventHandler ile bitmelidir
- Bir sınıfta MyEventHandler türünde bir olayı bildirmek için standart bir yöntem:
Evt1 alanı, delegate türündedir. event anahtar sözcüğü, bu alan üzerinde gerçekleştirilebilecek işlemleri kısıtlamak için kullanılmaktadır:
- C1 sınıfının dışından, yalnızca += ve -= işlemleri yapılabilir. Bu, olaya abone olan yöntemlerin (örneğin geliştiricinin hatası nedeniyle) silinmesini engeller. Olaya abone olmak (+=) veya abonelikten çıkmak (-=) mümkündür.
- Yalnızca C1 türünde bir örnek, Evt1. olayına abone olan yöntemlerin yürütülmesini tetikleyen Evt1(source,evtInfo) çağrısını gerçekleştirebilir
.NET çerçeve yapısı, bir olayın delegate imzasına uyan genel bir yöntem sağlar:
public delegate void EventHandler<TEventArgs>(object source, TEventArgs evtInfo) where TEventArgs : EventArgs
- delegate ve EventHandler, ikinci parametresinin türü olan TEventArgs genel türünü kullanır
- TEventArgs türü, EventsArgs türünden türetilmelidir (burada TEventArgs: EventArgs)
Bu genel delegate türüyle, C sınıfında bir X olayının tanımlanması aşağıdaki önerilen şemaya uygun olacaktır:
- X olayına ilişkin bilgileri kapsüllemek için EventArgs'ten türetilmiş bir XEventArgs türü tanımlayın
- C sınıfında EventHandler<XEventArgs> türünde bir olay tanımlayın.
- C sınıfında korumalı bir yöntem tanımlayın
tanımlayın; bu yöntem, X olayını abonelere "yayınlamak" için kullanılır.
Aşağıdaki örneği ele alalım:
- Emetteur sınıfı bir sıcaklığı kapsüller. Bu sıcaklık izlenir. Bu sıcaklık belirli bir eşiği aştığında bir olay tetiklenmelidir. Bu olaya TemperatureTropHaute adını vereceğiz. Bu olayla ilgili bilgiler TemperatureTropHauteEventArgs türünde kapsüllenecektir.
- Souscripteur sınıfı, önceki olaya abone olur. Olay hakkında bilgilendirildiğinde, konsolda bir mesaj görüntüler.
- Bir konsol programı bir yayıncı ve iki abone oluşturur. Klavyeden sıcaklık değerlerini alır ve bunları bir Emetteur örneğine kaydeder. Sıcaklık çok yüksekse, Emetteur örneği TemperatureTropHaute olayını yayınlar.
Olay yönetimine ilişkin önerilen yönteme uymak için, öncelikle olay bilgilerini kapsüllemek üzere TemperatureTropHauteEventArgs türünü tanımlıyoruz:
using System;
namespace Chap6 {
public class TemperatureTropHauteEventArgs:EventArgs {
// olay sırasındaki sıcaklık
public decimal Temperature { get; set; }
// üreticiler
public TemperatureTropHauteEventArgs() {
}
public TemperatureTropHauteEventArgs(decimal temperature) {
Temperature = temperature;
}
}
}
- 6. satır: TemperatureTropHauteEventArgs sınıfı tarafından kapsüllenmiş bilgi, TemperatureTropHaute olayını tetikleyen sıcaklıktır.
Emetteur sınıfı şu şekildedir:
using System;
namespace Chap6 {
public class Emetteur {
static decimal SEUIL = 19;
// gözlemlenen sıcaklık
private decimal temperature;
// kaynak adı
public string Nom { get; set; }
// olay bildirildi
public event EventHandler<TemperatureTropHauteEventArgs> TemperatureTropHaute;
// sıcaklık okuma/yazma
public decimal Temperature {
get {
return temperature;
}
set {
temperature = value;
if (temperature > SEUIL) {
// olay abonelere bildirilir
OnTemperatureTropHaute(new TemperatureTropHauteEventArgs(temperature));
}
}
}
// olay bildirimi
protected virtual void OnTemperatureTropHaute(TemperatureTropHauteEventArgs evt) {
// TemperatureTropHaute olayının abonelere iletilmesi
TemperatureTropHaute(this, evt);
}
}
}
- 5. satır: TemperatureTropHaute olayının yayınlanacağı sıcaklık eşiği.
- 10. satır: Vericinin tanımlanması için bir adı vardır
- 12. satır: TemperatureTropHaute olayı.
- satır 15-26: Sıcaklığı veren get yöntemi ve bunu kaydeden set yöntemi. set yöntemi, kaydedilecek sıcaklık 5. satırdaki eşiği aşarsa TemperatureTropHaute olayını yayınlar. Bu yöntem, eşiği aşan sıcaklığın kaydedildiği bir TemperatureTropHauteEventArgs nesnesini parametre olarak geçirerek, 29. satırdaki OnTemperatureTropHauteHandler yöntemi aracılığıyla olayı yayınlar.
- 29-32. satırlar: TemperatureTropHaute olayı, birinci parametre olarak vericinin kendisini ve ikinci parametre olarak parametre olarak alınan TemperatureTropHauteEventArgs nesnesini kullanarak yayınlanır.
TemperatureTropHaute olayına abone olacak Souscripteur sınıfı şöyledir:
using System;
namespace Chap6 {
public class Souscripteur {
// ad
public string Nom { get; set; }
// TemperatureTropHaute olay yöneticisi
public void EvtTemperatureTropHaute(object source, TemperatureTropHauteEventArgs e) {
// operatör konsolu ekranı
Console.WriteLine("Souscripteur [{0}] : la source [{1}] a signalé une température trop haute : [{2}]", Nom, ((Emetteur)source).Nom, e.Temperature);
}
}
}
- 6. satır: Her abone bir adla tanımlanır.
- 9-12. satırlar: TemperatureTropHaute olayına eşleştirilecek yöntem. Bu yöntemin, bir olay yöneticisinin sahip olması gereken delegate EventHandler<TEventArgs> türünde bir imzası vardır. Yöntem, konsolda şunları görüntüler: mesajı görüntüleyen abonenin adı, olayı bildiren yayıncının adı ve olayı tetikleyen sıcaklık.
- TemperatureTropHaute olayına, Emetteur nesnesinin aboneliği, Souscripteur sınıfında yapılmamaktadır. Bu, harici bir sınıf tarafından yapılacaktır.
[Program.cs] programı tüm bu öğeleri birbirine bağlar:
using System;
namespace Chap6 {
class Program {
static void Main(string[] args) {
// olay vericisi oluşturma
Emetteur e1 = new Emetteur() { Nom = "e" };
// 2 aboneye sahip bir tablo oluşturma
Souscripteur[] souscripteurs = new Souscripteur[2];
for (int i = 0; i < souscripteurs.Length; i++) {
// abone oluşturma
souscripteurs[i] = new Souscripteur() { Nom = "s" + i };
// e1'den TemperatureTropHaute olayına abone oluyor
e1.TemperatureTropHaute += souscripteurs[i].EvtTemperatureTropHaute;
}
// klavyeden sıcaklıklar okunuyor
decimal temperature;
Console.Write("Température (rien pour arrêter) : ");
string saisie = Console.ReadLine().Trim();
// girdilen satır boş olmadığı sürece
while (saisie != "") {
// Girilen değer ondalık bir sayı mı?
if (decimal.TryParse(saisie, out temperature)) {
// sıcaklık doğru - kaydedilir
e1.Temperature = temperature;
} else {
// hata bildirilir
Console.WriteLine("Température incorrecte");
}
// yeni giriş
Console.Write("Température (rien pour arrêter) : ");
saisie = Console.ReadLine().Trim();
}//while
}
}
}
- 6. satır: yayıncının oluşturulması
- 8-14. satırlar: Yayıcının TemperatureTropHaute olayına abone olacak iki abone oluşturulur.
- 20-32. satırlar: klavyeden sıcaklık girişi döngüsü
- 24. satır: Girilen sıcaklık doğruysa, Emetteur e1 nesnesine iletilir; sıcaklık 19 °C'den yüksekse bu nesne TemperatureTropHaute olayını tetikler.
Çalıştırma sonuçları şöyledir:

