Я делаю пользовательские события для C#, и иногда он не работает.
Это - то, как я заставляю событие произойти:
private bool isDoorOpen;
public bool IsDoorOpen {
get { return isDoorOpen;}
private set { isDoorOpen = value; DoorsChangeState(this, null);}
}
И это объявления события:
//events
public delegate void ChangedEventHandler(Elevator sender, EventArgs e);
public event ChangedEventHandler PositionChanged;
public event ChangedEventHandler DirectionChanged;
public event ChangedEventHandler BreaksChangeState;
public event ChangedEventHandler DoorsChangeState;
Это работает, пока существуют методы, присоединенные к событиям, но если нет, это бросает пустой указатель касательно исключения. Что я делаю неправильно?
Рекомендуемый способ вызова события -
var handler = this.DoorsChangeState;
if (handler != null)
handler(this, null);
Причина копирования обработчика локально - это случай, если обработчик события изменится в другом потоке, пока вы проверяете null.
EDIT: Нашел статью, в которой говорится об условиях гонки. http://blogs.msdn.com/ericlippert/archive/2009/04/29/events-and-races.aspx
Я знаю, что этот вопрос обсуждался (и отвечал) несколько раз здесь, на SO.
Также где-то здесь у меня есть следующие методы расширения, чтобы упростить использование этого шаблона:
public static class EventHandlerExtensions
{
public static void FireEvent<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
var temp = handler;
if (temp != null)
{
temp(sender, args);
}
}
public static void FireEvent(this EventHandler handler, object sender)
{
var temp = handler;
if (temp != null)
{
temp(sender, EventArgs.Empty);
}
}
}
Итак, в вашем коде вы можете сказать:
public bool IsDoorOpen
{
get { return isDoorOpen;}
private set
{
isDoorOpen = value;
DoorsChangeState.FireEvent(this);
}
}
Вам нужно проверить, было ли событие подписано.
Я использую эту стандартную форму для сброса всех моих событий.
var temp = EventName;
if(EventName!= null)
temp(this, null);
Если событие не подписано на момент его возникновения, будет выброшен NullReferenceException. Это правильное поведение, а не то, что вы сделали неправильно.
Вы должны проверить:
if(DoorsChangeState != null)
{
DoorsChangeState(this, null); // Only fire if subscribed to
}
Перед вызовом события вы должны проверить, не является ли событие нулевым:
if (DoorsChangeState != null)
DoorsChangeState(this, null);
Когда DoorsChangeState
равно null, это означает, что нет слушателей этого события.