Как повысить пользовательское событие из статического класса
У меня есть статический класс, который я хотел бы создать событие как часть блока catch try в статическом методе этого класса.
Например, в этом методе я хотел бы поднять пользовательское событие в catch.
public static void saveMyMessage(String message)
{
try
{
//Do Database stuff
}
catch (Exception e)
{
//Raise custom event here
}
}
Спасибо.
Ответы
Ответ 1
Важно: будьте очень осторожны с подпиской на статическое событие из экземпляров. Статический-статический - это хорошо, но подписка от статического события на обработчик экземпляра - отличный (читаемый: очень опасный) способ сохранить этот экземпляр живым навсегда. GC увидит ссылку и не будет собирать экземпляр, если вы не отмените подписку (или используйте что-то вроде WeakReference).
Шаблон для создания статических событий совпадает с событиями isntance, только с static
:
public static event EventHandler SomeEvent;
Чтобы облегчить жизнь (повторная проверка), полезный трюк здесь заключается в добавлении тривиального обработчика:
public static event EventHandler SomeEvent = delegate {};
Затем вы можете просто вызвать его без нулевой проверки:
SomeEvent(null, EventArgs.Empty);
Обратите внимание, что поскольку экземпляры делегата являются неизменяемыми, а удаление ссылок является потокобезопасным, здесь никогда не существует условия гонки, и нет необходимости блокировать... кто когда-либо подписывается при вызове отмены ссылки.
(отрегулируйте свои собственные события-аргументы и т.д.).
Этот трюк применим в равной степени к событиям экземпляра.
Ответ 2
Ваше событие также должно быть статическим:
public class ErrorEventArgs : EventArgs
{
private Exception error;
private string message;
public ErrorEventArgs(Exception ex, string msg)
{
error = ex;
message = msg;
}
public Exception Error
{
get { return error; }
}
public string Message
{
get { return message; }
}
}
public static class Service
{
public static EventHandler<ErrorEventArgs> OnError;
public static void SaveMyMessage(String message)
{
EventHandler<ErrorEventArgs> errorEvent = OnError;
if (errorEvent != null)
{
errorEvent(null, new ErrorEventArgs(null, message));
}
}
}
И использование:
public class Test
{
public void OnError(object sender, ErrorEventArgs args)
{
Console.WriteLine(args.Message);
}
}
Test t = new Test();
Service.OnError += t.OnError;
Service.SaveMyMessage("Test message");
Ответ 3
Несколько человек предложили примеры кода, просто не запускайте событие с использованием кода, например:
if(null != ExampleEvent)
{
ExampleEvent(/* put parameters here, for events: sender, eventArgs */);
}
поскольку это содержит условие гонки между тем, когда вы проверяете событие на null и когда вы действительно запускаете событие. Вместо этого используйте простую вариацию:
MyEvent exampleEventCopy = ExampleEvent;
if(null != exampleEventCopy)
{
exampleEventCopy(/* put parameters here, for events: sender, eventArgs */);
}
Это скопирует всех подписчиков событий в exampleEventCopy, которые затем можно использовать как локальную версию публичного события, не беспокоясь о каких-либо условиях гонки (по сути, возможно, что другой поток может вас упредить сразу после того, как вы проверили публичное событие на null и приступите к удалению всех подписчиков из события, в результате чего последующее включение события вызовет исключение, используя копию локального кода, вы избегаете возможности использования других потоков, поскольку они не могут получить доступ к локальной переменной).
Ответ 4
Примечание: VS2008, С#
Просто объявите событие, как обычно, в статическом классе, но обязательно отметьте это событие как статическое:
public static event EventHandler Work;
Затем просто подпишитесь на него, как обычно.
Ответ 5
Просто добавьте "Делегаты неизменяемы" Итак, как показано в приведенном выше примере, следующая строка получает копию делегата.
EventHandler<ErrorEventArgs> errorEvent = OnError;