GÜNCELLEME
C # 6'dan itibaren, bu sorunun cevabı :
SomeEvent?.Invoke(this, e);
Aşağıdaki tavsiyeleri sık sık duyuyorum / okuyorum:
Bir etkinliği kontrol etmeden null
ve tetiklemeden önce her zaman bir kopyasını oluşturun . Bu, etkinliğin null
null olup olmadığını kontrol ettiğiniz yerle olayı tetiklediğiniz yer arasındaki noktada gerçekleşme olasılığını ortadan kaldırır :
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
Güncelleme : Optimizasyonları okuduğumda bunun da olay üyesinin değişken olmasını gerektirebileceğini düşündüm, ancak Jon Skeet cevabında CLR'nin kopyayı optimize etmediğini belirtti.
Ancak bu arada, bu sorunun ortaya çıkması için başka bir iş parçacığının böyle bir şey yapmış olması gerekir:
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
Gerçek dizi bu karışım olabilir:
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
OnTheEvent
Yazarın aboneliği iptal edildikten sonra devam eden nokta , yine de bunun olmasını önlemek için özel olarak aboneliği iptal edilir. Elbette gerçekten ihtiyaç duyulan şey, uygun senkronizasyona sahip özel bir etkinlik uygulamasıdır.add
ve remove
erişimcilerde . Ayrıca, bir olay tetiklenirken bir kilit tutulursa olası kilitlenme sorunu vardır.
Peki bu Kargo Kült Programlama mı? Öyle görünüyor - bir çok insan kodlarını birden fazla iş parçacığından korumak için bu adımı atmalı, gerçekte olayların çok iş parçacıklı bir tasarımın parçası olarak kullanılmadan önce bundan daha fazla bakım gerektirdiğini düşünüyorum. . Sonuç olarak, bu ek bakımı almayan insanlar da bu tavsiyeyi görmezden gelebilir - bu sadece tek iş parçacıklı programlar için bir sorun değildir ve aslında,volatile
çoğu çevrimiçi örnek kodun , tavsiyenin hiç etkisi.
(Ve delegate { }
üye beyanında boş olanı atamak çok daha kolay değil mi?null
ilk etapta mi?)
Güncellenmiş:Açık değilse, her koşulda null referans istisnasından kaçınmak için tavsiyenin niyetini kavradım. Demek istediğim, bu özel null referans istisnasının yalnızca olaydan başka bir iş parçacığı çıkarması durumunda ortaya çıkabilir ve bunu yapmanın tek nedeni, bu olay aracılığıyla başka teknik çağrıların alınmamasını sağlamaktır. . Bir yarış koşulunu gizleyecektiniz - ortaya çıkarmak daha iyi olurdu! Bu boş istisna, bileşeninizin kötüye kullanımını tespit etmeye yardımcı olur. Bileşeninizin kötüye kullanıma karşı korunmasını istiyorsanız, WPF örneğini takip edebilirsiniz - iş parçacığının yapıcısında depolanıp başka bir iş parçacığı doğrudan bileşeninizle etkileşime girmeye çalışırsa bir istisna atabilirsiniz. Ya da gerçekten güvenli bir bileşen uygulayın (kolay bir iş değil).
Bu yüzden sadece bu kopyalama / kontrol deyimini yapmanın, kodunuza karışıklık ve gürültü ekleyerek kargo kült programlama olduğunu iddia ediyorum. Aslında diğer ipliklere karşı korunmak çok daha fazla iş gerektirir.
Eric Lippert'in blog gönderilerine yanıt olarak güncelleme:
Bu yüzden olay işleyicileri hakkında kaçırdığım önemli bir şey var: "olay işleyicileri, abonelik iptal edildikten sonra bile çağrılması karşısında sağlam olmalı" ve bu nedenle yalnızca olayın olasılığını önemsememiz gerekiyor. delege olmak null
. Olay işleyicileri için bu gereksinim herhangi bir yerde belgelenmiş mi?
Ve böylece: "Bu sorunu çözmenin başka yolları da var; örneğin, işleyicinin hiçbir zaman kaldırılmamış boş bir eylem için başlatılması. Ancak boş bir denetim yapmak standart modeldir."
Öyleyse sorumun kalan kısmı, "standart kalıp" için neden açık-boş-kontrol? Boş delege atayan alternatif, yalnızca = delegate {}
olay bildirimine eklenmeyi gerektirir ve bu, olayın yapıldığı her yerden küçük kokmuş tören yığınlarını ortadan kaldırır. Boş delegenin somutlaştırılması ucuz olduğundan emin olmak kolay olacaktır. Yoksa hala bir şey mi kaçırıyorum?
Şüphesiz (Jon Skeet'in önerdiği gibi) bu, 2005'te yapması gerektiği gibi, ölmemiş olan sadece .NET 1.x tavsiyesi olmalı?
EventName(arguments)
etkinliğin temsilcisini koşulsuz olarak çağırmaya karar vermesidir, yalnızca temsilci yalnızca boş değilse çağırır (boş değilse hiçbir şey yapmaz).