Как получить доступ к объекту, к которому прикреплен таймер? - PullRequest
1 голос
/ 03 октября 2011

У меня проблема программирования, которая, по-моему, вызвана моей ржавостью при использовании событий и делегатов ...

У меня есть код:

public void DoStuff()
        {
            List<IProcess> processorsForService1  = processorsForService1 = ProcessFactory.GetProcessors();

            foreach (IProcess p in processorsForService1)
            {
                if (p.ProcessTimer != null)
                {
                    p.ProcessTimer.Elapsed += new ElapsedEventHandler(IProcess_Timer_Elapsed);
                }
            }
        }

И:

  private void IProcess_Timer_Elapsed(object sender, ElapsedEventArgs e)
            {
                IProcess p = (IProcess)sender;
                p.Step_One();
                p.Step_Two();
            }

Но когда я добираюсь до обработчика событий, я получаю исключение нулевой ссылки для p в первой строке.

Как передать аргумент обработчику в этом случае?

Ответы [ 4 ]

1 голос
/ 03 октября 2011

Похоже, вы используете System.Timers.Timer, если бы вы использовали System.Threading.Timer, то вы могли бы передать объект state, который в данном случае , может быть желаемым экземпляром класса, то есть «владельцем» таймера. Таким образом, вы определяете тело вашего метода так же, как в предыдущем опыте реализации в обработчике событий, только теперь подпись выглядит следующим образом:

private void MyTimerCallbackMethod(object state)
{

}

Затем, после создания экземпляра таймера, вы можете сделать что-то вроде:

var timerCallback = new TimerCallback(MyTimerCallback);
var timer = new Timer(timerCallback, myStateObject, 
    Timeout.Infinite, Timeout.Infinite);

Затем используйте timer.Change(whenToStart, interval), чтобы отключить таймер.

0 голосов
/ 03 октября 2011

Сделайте обработчик событий членом IProcess и настройте его следующим образом:

p.ProcessTimer.Elapsed += new ElapsedEventHandler(p.IProcess_Timer_Elapsed);

и если вы хотите обработать событие в другом месте, сделайте обработчик пересылки события:

class IProcess
{
  public delegate void Timer_Elapsed_Handler (IProcess process, ElapsedEventArgs e);
  public event Timer_Elapsed_Handler Timer_Elapsed;

  public void IProcess_Timer_Elapsed (object sender, ElapsedEventArgs e)
  {
    if (Timer_Elapsed != null) Timer_Elapsed (this, e);
  }
}
0 голосов
/ 03 октября 2011

если бы вы использовали лямбду вместо делегата, вы могли бы ссылаться на класс из лямбды, потому что он все еще будет в среде ссылок.Я думаю, что это называется Закрытие .

public void DoStuff()
{
    List<IProcess> processorsForService1 = ProcessFactory.GetProcessors();
    foreach (IProcess p in processorsForService1)
    {
        if (p.ProcessTimer != null)
        {
            p.ProcessTimer.Elapsed += (s, e) =>
            {
                p.Step_One();
                p.Step_Two();
            };
        }
    }
}

Хотя остерегайтесь следующих правил, относящихся к области действия (взято из msdn ):

Следующие правила применяются к области переменных в лямбда-выражениях:

  • Захваченная переменная не будет собираться мусором до тех пор, пока делегат, ссылающийся на нее, не выйдет за пределы области действия.

  • Переменные, введенные в лямбда-выражении, не видны во внешнем методе.

  • Лямбда-выражение не может напрямую захватить параметр ref или out из метода вложения.

  • Оператор return в лямбда-выражении не приводит к возврату метода вложения.

  • Лямбда-выражение не может содержать оператор goto, breakоператор или оператор продолжения, цель которого находится вне тела или в теле содержащейся анонимной функции.

0 голосов
/ 03 октября 2011

Отправителем является объект таймера, а не объект, связанный с делегатом обработки. Для начала события могут иметь несколько обработчиков.

Что вы можете сделать, это создать делегата, который имеет доступ к IProcess с помощью захвата переменных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...