1. Eventlərin elan edilməsi və adlandırma tərzi
Eventlər — sadəcə delegatlar deyil. Bu tətbiqin hissələri arasında kommunikasiya üçün ayrıca bir varlıqdır və onun elan edilməsi aydın olmalıdır.
Düzgün delegat tipindən istifadə edin
99% hallarda standart delegatlardan istifadə edin:
- EventHandler — data olmayan eventlər üçün.
- EventHandler<TEventArgs> — parametr keçirmək lazım olduqda.
Standartlaşma kodun dəstəyini və .NET kitabxanalarla inteqrasiyanı asanlaşdırır. Əgər EventHandler uyğun gəlirsə, öz delegatınızı icad etməyin.
public event EventHandler SomethingHappened; // Data yoxdur
public event EventHandler<MyEventArgs> DataReceived; // Əlavə data var
Xüsusi customizasiya lazımdırsa — öz delegatınızı elan edin, amma bu nadir hallardır.
Eventlərin adlandırılması
.NET-də eventlər keçmiş zamanda adlanır: Completed, Clicked, Changed, Received. Bu baş vermiş hadisəni vurğulayır.
Nümunələr:
public event EventHandler DataLoaded; // Məlumat yükləndi
public event EventHandler<MessageEventArgs> MessageReceived; // Mesaj qəbul olundu
public event EventHandler Saving; // Saxlama prosesi başladı
Bəzən "dən əvvəl" baş verənlər üçün Changing forması istifadə olunur ki, müdaxilə etmək imkanı olsun.
2. Publisher sinfinin təşkili: virtual metod OnEvent
Həmişə protected virtual metod əlavə edin ki, eventi çağırsın: mərkəzləşdirilmiş çağırış nöqtəsi, miras alma zamanı genişləndirmə və davranışın proqnozlaşdırıla bilməsi üçün.
public class FileLoader
{
public event EventHandler<FileLoadedEventArgs> FileLoaded;
protected virtual void OnFileLoaded(FileLoadedEventArgs e)
{
FileLoaded?.Invoke(this, e);
}
public void Load(string filename)
{
// ... fayl yükləmə məntiqi ...
OnFileLoaded(new FileLoadedEventArgs(filename));
}
}
public class FileLoadedEventArgs : EventArgs
{
public string FileName { get; }
public FileLoadedEventArgs(string fileName) => FileName = fileName;
}
Yalnız OnFileLoaded eventi çağırsın — beləliklə təqib və test etmək daha asandır.
3. Abonəlik və ayırılma qaydaları: yaşam dövrü, IDisposable
Əgər subscriber-in ömrü publisher-dən qısadırsa, subscriber məhv olmadan əvvəl mütləq ayrılmalıdır. IDisposable implement etmək və Dispose()-də ayrılmaq rahatdır.
public class TemporaryListener : IDisposable
{
private readonly Publisher _publisher;
public TemporaryListener(Publisher publisher)
{
_publisher = publisher;
_publisher.DataReceived += HandleData;
}
private void HandleData(object sender, EventArgs e)
{
// Data ilə işləmə
}
public void Dispose()
{
_publisher.DataReceived -= HandleData;
}
}
// using ilə istifadə:
using (var listener = new TemporaryListener(myPublisher))
{
// listener burada eventləri dinləyir
}
// using-dən çıxdıqdan sonra - Dispose çağırıldı, ayrılma baş verdi
Əgər ayrılma unudularsa, publisher subscriber-in delegatına referans saxlayacaq — yaddaş sızması və "zombi-obyektlər" yaranacaq.
4. Eventlərin thread-safe çağırışı
Multithreaded kodda subscriber-lər event çağırışı ilə paralel əlavə/çıxarıla bilər. Bu yarışlara və NullReferenceException-ə səbəb ola bilər. Thread-safe nümunədən istifadə edin: delegatı lokal dəyişkəndə kopyalayın.
protected virtual void OnSomethingHappened()
{
EventHandler handler = SomethingHappened;
handler?.Invoke(this, EventArgs.Empty);
}
C# 6+ ilə kifayətdir:
SomethingHappened?.Invoke(this, EventArgs.Empty);
5. EventArgs-dən istifadə edin, object yox
Dataları object və ya sinif sahələri vasitəsilə ötürməyin. EventArgs-in törəmələri ilə güclü tipizasiya istifadə edin.
public class DownloadCompletedEventArgs : EventArgs
{
public string FileName { get; }
public long Size { get; }
public DownloadCompletedEventArgs(string fileName, long size)
{
FileName = fileName;
Size = size;
}
}
public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted;
6. Eventlər və subscriber-lərin sənədləşdirilməsi
Sənədləşdirin: event nə vaxt çağırılır, EventArgs sahələrinin mənası, ayrılma lazım olub-olmadığı və nə vaxt.
/// <summary>
/// Event müvəffəqiyyətli data yüklənməsindən sonra yaranır.
/// </summary>
public event EventHandler<DataLoadedEventArgs> DataLoaded;
7. Event arxitekturası üzrə ümumi tövsiyələr
Məsuliyyətləri ayırın
Publisher yalnız hadisəni bildirir. Subscriber özü qərar verir ki, nə vaxt abonə və nə vaxt ayrılmaq lazımdır.
Event bombardmanından çəkinin
Eyni eventi saniyədə onlarla dəfə lazım olmadan yaratmayın — bu artıq yük yaradır.
Eventləri iki tərəfli əlaqə üçün istifadə etməməyə çalışın
Eventlər "bir xəbər verir — çoxu dinləyir" sxemi üçün uyğundur. İki tərəfli kommunikasiya üçün interface-lərə, callbacks və ya digər mexanizmlərə baxın.
Subscriber-lərə açıq referanslar saxlamayın
Subscriber-lərə açıq referanslar saxlamayın — eventlər və delegatlar bunu avtomatik edəcək.
8. Klassik antipatternlər
Tipsiz eventlər
public event Action<object> SomethingHappened; // İçində nə olduğu aydın deyil
Pis: tipizasiya pozulur, cast-lar lazım olur, dəstəklənməsi çətinləşir.
Ayrılma unutuldu
public class ShortLivedListener
{
public ShortLivedListener(Publisher p) =>
p.DataReceived += DoWork;
private void DoWork(object sender, EventArgs e) { /* ... */ }
// Dispose yoxdur, ayrılma yoxdur => zombi-obyektlər!
}
SRP-nin pozulması
Sinif həm publisher, həm subscriber, həm də handler kimi çıxış edir — rolların qarışması. Məsuliyyəti ayırın.
9. Praktik tətbiq — müsahibələrdə və layihələrdə
Çox layihələrdə publish-subscribe mexanizminin düzgün təşkili skalabilitə və maintenansı təmin edir. Müsahibələrdə tez-tez istəyirlər:
- event sistemini düzgün tipizasiya ilə reallaşdırmaq,
- subscriber-lərin yaşam dövrünü idarə etməyi göstərmək,
- eventlərin thread-safe çağırışını izah etmək.
Təmiz, sənədlənmiş və düzgün təşkil edilmiş event kodu sizi namizədlər arasında dərhal seçəcək.
GO TO FULL VERSION