Лямбды в рамках Дополнительных методов: Возможная утечка памяти?

Я просто дал ответ на довольно простой вопрос при помощи дополнительного метода. Но после записи его я помнил, что Вы не можете отказаться от подписки лямбда от обработчика событий.

До сих пор никакая большая проблема. Но как все это ведет себя в рамках дополнительного метода??

Ниже мой код, отрезанный снова. Таким образом, кто-либо может просветить меня, если это приведет к несметным числам таймеров, бродящих вокруг в памяти при вызове этого дополнительного метода многократно?

Я сказал бы "нет", вызвал бы объем таймера, ограничен в этой функции. Таким образом, после отъезда его ни у кого больше нет ссылки на этот объект. Я просто немного не уверен, вызываю, мы здесь в статической функции в статическом классе.

public static class LabelExtensions
{
    public static Label BlinkText(this Label label, int duration)
    {
        System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

        timer.Interval = duration;
        timer.Tick += (sender, e) =>
            {
                timer.Stop();
                label.Font = new Font(label.Font, label.Font.Style ^ FontStyle.Bold);
            };

        label.Font = new Font(label.Font, label.Font.Style | FontStyle.Bold);
        timer.Start();

        return label;
    }
}

Обновление

Просто для уточнения я использовал Систему. Windows. Формы. Таймер. Таким образом из Ваших ответов это кажется, это особенно использующее этот класс таймера просто было причиной правильного выбора, это делает что-либо путь, поскольку я ожидал бы это в этом случае. При попытке другого класса таймера в этом случае, можно столкнуться с проблемой как, узнанный Matthew. Также я нашел путь при помощи WeakReferences, если мои объекты остаются в живых или нет.

Обновление 2

После небольшого сна и немного большего количества взглядов, я также внес другое изменение в свой тестер (ответ ниже), я просто добавил a GC.Collect() после последней строки и набора продолжительность к 10 000. После запуска BlinkText() несколько раз я hitted все время мой button2, чтобы получить текущее состояние и вызвать сборку "мусора". И поскольку кажется, что все таймеры будут уничтожены после вызова Stop() метод. Таким образом, также сборка "мусора", в то время как мой BlinkText уже покидают и таймер, работает, не приводит ни к каким проблемам.

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

6
задан Community 23 May 2017 в 12:30
поделиться

7 ответов

После прочтения всех ваших ответов я нашел способ лучше проверить это.

Я обновил свой класс Extension следующим образом:

public static class LabelExtensions
{
    public static List<WeakReference> _References = new List<WeakReference>();

    public static Label BlinkText(this Label label, int duration)
    {
        Timer timer = new Timer();

        _References.Add(new WeakReference(timer));

        timer.Interval = duration;
        timer.Tick += (sender, e) =>
        {
            timer.Stop();
            label.Font = new Font(label.Font, label.Font.Style ^ FontStyle.Bold);
        };

        label.Font = new Font(label.Font, label.Font.Style | FontStyle.Bold);
        timer.Start();

        return label;
    }
}

Также я создал вторую кнопку, вторую метку и добавил следующий код в событие click:

    private void button2_Click(object sender, EventArgs e)
    {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < LabelExtensions._References.Count; i++)
        {
            var wr = LabelExtensions._References[i];
            sb.AppendLine(i + " alive: " + wr.IsAlive);
        }

        label2.Text = sb.ToString();
    }

Теперь я просто нажал на первую кнопку несколько раз, чтобы первая метка мигнула. Затем я нажал на вторую кнопку и получил список, в котором я могу посмотреть, живы ли мои таймеры. При первом нажатии все они получили значение true. Но затем я снова несколько раз нажал на первую кнопку и, когда обновил сообщение о состоянии, увидел, что первые элементы уже получили false в IsAlive. Так что я могу с уверенностью сказать, что эта функция не приводит к проблемам с памятью.

0
ответ дан 10 December 2019 в 00:36
поделиться

Утечки памяти здесь не ваша проблема. Сборщик мусора может удалить объект таймера. Но пока таймер не остановлен, похоже, есть ссылка на таймер, поэтому он не утилизируется преждевременно. Чтобы проверить это, унаследуйте класс MyTimer от Timer, переопределите Dispose (bool) и установите точку останова. После BlinkText вызовите GC.Collect и GC.WaitForPendingFinalizers. После второго вызова первый экземпляр таймера удаляется.

2
ответ дан 10 December 2019 в 00:36
поделиться

Вы должны удалить Timer явно, вызвав его метод Dispose, или неявно, вызвав Stop после его использования, чтобы ваша реализация была правильной и никогда не вызывала утечки памяти.

0
ответ дан 10 December 2019 в 00:36
поделиться

Я только что провел забавный тест в соответствии с тем, что говорил @ cornerback84 . Я создал форму с меткой и двумя кнопками. Одна кнопка привязывает ваш метод расширения, другая - принудительную GC.Collect () . Затем я удалил timer.Stop () в вашем цикле событий. Было действительно весело нажать на кнопку «Пуск» несколько раз. Интервал моргания сильно запутался.

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

Изменить: ...

Начнем веселье ... это также может зависеть от Таймера , который вы используете.

  • System.Windows.Forms.Timer => этот не будет собирать, но будет нормально работать с насосом системных сообщений. Если вы вызовете .Stop () , этот объект может получить право на сбор.

  • System.Timers.Timer => при этом не используется насос сообщений. Многие также называют насос сообщений. Это не будет собирать . Если вы вызовете .Stop () , этот объект может получить право на сбор.

  • System.Threading.Timer => Для этого требуется вызвать насос сообщений. Но это также остановится на GC.Collect () (эта версия, похоже, не просочилась)

1
ответ дан 10 December 2019 в 00:36
поделиться

Ваш код правильный и не приведет к утечке памяти или ресурсов, но только , потому что вы останавливаете таймер в обработчике событий. Если вы закомментируете // timer.Stop (); , он будет продолжать мигать, даже если позже вы выполните GC.Collect (); .

Таймер выделяет окно для прослушивания сообщений WM_ и каким-то образом этим закрепляется. Когда таймер остановлен ( Enabled = false ), он освобождает это окно.
Контекст статического или метода расширения роли не играет.

3
ответ дан 10 December 2019 в 00:36
поделиться

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

0
ответ дан 10 December 2019 в 00:36
поделиться

Ваше предположение верно. Таймер будет выделен при вызове метода и будет иметь право на сборку мусора в конце.

Обработчик события lamba (при условии, что он не упоминается в другом месте) также будет иметь право на сборку мусора.

Тот факт, что это статический метод и / или метод расширения, не меняет основных правил достижимости объекта.

4
ответ дан 10 December 2019 в 00:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: