Czy jest jakiś minus dodawania anonimowego pustego delegata w deklaracji zdarzenia?
Widziałem kilka wzmianek o tym idiomie (w tym Na SO):
// Deliberately empty subscriber
public event EventHandler AskQuestion = delegate {};
Plusa jest jasna-pozwala uniknąć konieczności sprawdzania null przed podniesieniem zdarzenia.
Jestem jednak ciekaw, czy są jakieś wady. na przykład, czy jest to coś, co jest w powszechnym użyciu i jest wystarczająco przejrzyste, że nie spowoduje bólu głowy w utrzymaniu? Czy jest jakieś zauważalne trafienie wydajności pustego połączenia abonenckiego zdarzenia?
9 answers
Jedynym minusem jest bardzo niewielka kara za wydajność, ponieważ nazywasz dodatkowego pustego delegata. Poza tym nie ma kary alimentacyjnej lub innych wad.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-04 20:59:31
Zamiast wywoływać narzuty wydajności, dlaczego nie użyć metody rozszerzenia , Aby złagodzić oba problemy:
public static void Raise(this EventHandler handler, object sender, EventArgs e)
{
if(handler != null)
{
handler(sender, e);
}
}
Po zdefiniowaniu nigdy nie musisz ponownie sprawdzać żadnego zdarzenia null:
// Works, even for null events.
MyButtonClick.Raise(this, EventArgs.Empty);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:24
W przypadku systemów, które intensywnie wykorzystują zdarzenia i mają kluczowe znaczenie dla wydajności , na pewno będziesz chciał przynajmniej rozważyć nie robić tego. Koszt podniesienia zdarzenia z pustym delegatem jest mniej więcej dwa razy większy niż koszt podniesienia zdarzenia z zerowym czekiem.
Oto kilka liczb uruchamiających benchmarki na mojej maszynie:
For 50000000 iterations . . .
No null check (empty delegate attached): 530ms
With null check (no delegates attached): 249ms
With null check (with delegate attached): 452ms
A oto kod, którego użyłem do uzyskania tych liczb:
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
public event EventHandler<EventArgs> EventWithDelegate = delegate { };
public event EventHandler<EventArgs> EventWithoutDelegate;
static void Main(string[] args)
{
//warm up
new Program().DoTimings(false);
//do it for real
new Program().DoTimings(true);
Console.WriteLine("Done");
Console.ReadKey();
}
private void DoTimings(bool output)
{
const int iterations = 50000000;
if (output)
{
Console.WriteLine("For {0} iterations . . .", iterations);
}
//with anonymous delegate attached to avoid null checks
var stopWatch = Stopwatch.StartNew();
for (var i = 0; i < iterations; ++i)
{
RaiseWithAnonDelegate();
}
stopWatch.Stop();
if (output)
{
Console.WriteLine("No null check (empty delegate attached): {0}ms", stopWatch.ElapsedMilliseconds);
}
//without any delegates attached (null check required)
stopWatch = Stopwatch.StartNew();
for (var i = 0; i < iterations; ++i)
{
RaiseWithoutAnonDelegate();
}
stopWatch.Stop();
if (output)
{
Console.WriteLine("With null check (no delegates attached): {0}ms", stopWatch.ElapsedMilliseconds);
}
//attach delegate
EventWithoutDelegate += delegate { };
//with delegate attached (null check still performed)
stopWatch = Stopwatch.StartNew();
for (var i = 0; i < iterations; ++i)
{
RaiseWithoutAnonDelegate();
}
stopWatch.Stop();
if (output)
{
Console.WriteLine("With null check (with delegate attached): {0}ms", stopWatch.ElapsedMilliseconds);
}
}
private void RaiseWithAnonDelegate()
{
EventWithDelegate(this, EventArgs.Empty);
}
private void RaiseWithoutAnonDelegate()
{
var handler = EventWithoutDelegate;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-11 23:12:01
Jeśli robisz to z /lot/, możesz chcieć mieć pojedynczy, statyczny / współdzielony pusty delegat, którego ponownie używasz, po prostu w celu zmniejszenia objętości wystąpień delegatów. Zauważ, że kompilator i tak buforuje tego delegata na zdarzenie (w polu statycznym), więc jest to tylko jedna instancja delegata na definicję zdarzenia, więc nie jest to zapis ogromny - ale może warto.
Pole per-instance w każdej klasie będzie oczywiście zajmowało tę samą przestrzeń.
Tzn.
internal static class Foo
{
internal static readonly EventHandler EmptyEvent = delegate { };
}
public class Bar
{
public event EventHandler SomeEvent = Foo.EmptyEvent;
}
Inne niż to, wydaje się w porządku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-04 22:15:58
Rozumiem, że pusty delegat jest bezpieczny dla wątku, podczas gdy sprawdzanie null nie jest.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 04:55:44
Powiedziałbym, że jest to trochę niebezpieczna konstrukcja, ponieważ kusi cię do zrobienia czegoś takiego jak:
MyEvent(this, EventArgs.Empty);
Jeśli klient wyrzuci wyjątek, serwer pójdzie z nim.
Więc może tak:
try
{
MyEvent(this, EventArgs.Empty);
}
catch
{
}
Ale jeśli masz wielu subskrybentów i jeden subskrybent rzuca wyjątek, co się stanie z innymi subskrybentami?
W tym celu używam statycznych metod pomocniczych, które sprawdzają null i połykają każdy wyjątek od strony Abonenta (to jest z idesign).
// Usage
EventHelper.Fire(MyEvent, this, EventArgs.Empty);
public static void Fire(EventHandler del, object sender, EventArgs e)
{
UnsafeFire(del, sender, e);
}
private static void UnsafeFire(Delegate del, params object[] args)
{
if (del == null)
{
return;
}
Delegate[] delegates = del.GetInvocationList();
foreach (Delegate sink in delegates)
{
try
{
sink.DynamicInvoke(args);
}
catch
{ }
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 21:47:43
Nie ma znaczącej kary za wydajność, o której można mówić, z wyjątkiem, być może, w niektórych ekstremalnych sytuacjach.
Zauważ jednak, że ta sztuczka staje się mniej istotna w C# 6.0, ponieważ Język zapewnia alternatywną składnię do wywoływania delegatów, które mogą być null:
delegateThatCouldBeNull?.Invoke(this, value);
Powyżej, operator warunkowy null ?.
łączy sprawdzanie null z wywołaniem warunkowym.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-10 18:23:23
Zamiast podejścia "empty delegate" można zdefiniować prostą metodę rozszerzenia, która enkapsuluje konwencjonalną metodę sprawdzania obsługi zdarzeń pod kątem null. Jest on opisany tutaj i Tutaj.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:18
Jedna rzecz została pominięta jako odpowiedź na to pytanie: niebezpieczne jest unikanie sprawdzania wartości null .
public class X
{
public delegate void MyDelegate();
public MyDelegate MyFunnyCallback = delegate() { }
public void DoSomething()
{
MyFunnyCallback();
}
}
X x = new X();
x.MyFunnyCallback = delegate() { Console.WriteLine("Howdie"); }
x.DoSomething(); // works fine
// .. re-init x
x.MyFunnyCallback = null;
// .. continue
x.DoSomething(); // crashes with an exception
Rzecz w tym, że nigdy nie wiadomo, kto użyje Twojego kodu w jaki sposób. Nigdy nie wiadomo, czy za kilka lat podczas naprawy Twojego kodu event/handler zostanie ustawiony na null.
Zawsze należy wpisać "if check".
Mam nadzieję, że to pomoże;)
Ps: dzięki za obliczenia wydajności.
Pps: edytował go z przypadku zdarzenia do i przykład wywołania zwrotnego. Dzięki za opinie... "Zakodowałem" przykład w / O Visual Studio i dostosowałem przykład, który miałem na myśli do zdarzenia. Przepraszam za zamieszanie.
Ppps: Nie wiem, czy nadal pasuje do wątku ... ale myślę, że to ważna zasada. Sprawdź również kolejny wątek stackflow
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:02