Таймер не расположен, когда форма - PullRequest
8 голосов
/ 31 октября 2011

Я пытаюсь понять, почему Windows.Forms.Timer не утилизируется, когда form, который его создал. У меня есть эта простая форма:

public partial class Form1 : Form {

    private System.Windows.Forms.Timer timer;

    public Form1() {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e) {
        timer = new Timer();
        timer.Interval = 1000;
        timer.Tick += new EventHandler(OnTimer);
        timer.Enabled = true;
    }

    private void OnTimer(Object source, EventArgs e) {
        Debug.WriteLine("OnTimer entered");
    }

    private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
        this.Dispose();
    }
}

Когда я закрываю его, вызывается this.Dispose, но событие срабатывания таймера продолжает вызываться. Я думал, что Dispose освобождает все объекты, принадлежащие удаленному объекту. Это неправда? Timer имеет определенное поведение?

Пока что я обнаружил, что способ избавиться от таймера - сделать timer.Tick -= OnTimer; - я вызываю его тогда в событии Form1_FormClosed. Это хорошее решение или я должен поступить иначе?

EDIT

Или просто лучше сделать:

private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
    timer.Dispose();
    this.Dispose();
}

Ответы [ 5 ]

12 голосов
/ 31 октября 2011

Как я уже говорил вам в моем предыдущем комментарии, вы должны попробовать:

private Form1_FormClosing(...)
{
    timer.Stop();
    timer.Tick -= new EventHandler(OnTimer);
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e) 
{
    timer.Dispose();
    timer = null;   
} 

Это хорошо, потому что вы предотвращаете повторный запуск таймера (в FormClosing) и можете проверять другие части (не в этом примере)потому что вы закрываете форму, но в качестве примера) если этот объект (таймер) был удален перед его использованием.
Так что в других частях вы можете сделать

if (timer != null) // Note: this is false if you just use timer.Dispose()
{
    ....
}
4 голосов
/ 31 октября 2011

Единственный правильный способ избавиться от одноразовых членов класса IDisposable состоит в том, чтобы сделать это внутри его метода Dispose(bool disposing) (см. Статью MSDN ).Другими словами, вы можете открыть автоматически сгенерированный файл Form.Designer.cs и поместить его в соответствующий метод.

С другой стороны, если вы добавите Timer через VS Designer (вместо того, чтобы создавать его самостоятельно),он будет добавлен в контейнер components:

// autogenerated inside Form.Designer.cs, InitializeComponent() method
this.timer = new System.Windows.Forms.Timer(this.components);

, а затем правильно утилизирован при удалении члена components:

// autogenerated inside Form.Designer.cs, Dispose(bool disposing) method
if (disposing && (components != null))
{
    components.Dispose();
}

Если вы хотите сделать это самостоятельно, сохранитеИмейте в виду, что дизайнер не создает components, если не считает, что это необходимо.Таким образом, components может быть null в вашем случае.

Самый простой способ решить эту проблему: добавить таймер, перетащив его из панели инструментов, а затем запустить его внутри обработчика Form_Load.

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

Простое Timer.Dispose() удаляет ресурсы таймера, включая остановку таймера в будущем.

Однако возможно, что после возврата Dispose() существуют обратные вызовы, которые либо активно выполняются, либо находятся в рабочей очереди пула потоков, ожидающих выполнения.

Вторая перегрузка, Timer.Dispose(WaitHandle) сообщит о переданном объекте после завершения всех обратных вызовов для таймера.Это может быть любой WaitHandle, например ManualResetEvent.

Чтобы упростить вещи, вы можете передать WaitHandle.InvalidHandle и Timer.Dispose() вернется только после завершения всех обратных вызовов.Это избавляет от необходимости выделять истинный объект события и обычно является тем, что вы хотите сделать.

Поскольку WaitHandle является абстрактным, вам нужно использовать небольшой хак, который предназначен для создания вашего собственного подкласса.

class InvalidWaitHandle : WaitHandle {}
Timer tmr = new Timer(...);
tmr.Dispose(new InvalidWaitHandle);
1 голос
/ 31 октября 2011

EventHandler - это постоянная ссылка, удалите ссылку и остановите таймер для события Closing или как только оно не требуется. Если вы хотите проверить, установлен ли таймер, проверьте его в закрытом событии

1 голос
/ 31 октября 2011
 private void Form1_FormClosed(object sender, FormClosedEventArgs e) 
 {        timer.Stop();    } 

Я не должен ставить this.Dispose ();в случае закрытия формы просто остановите таймер.

...