Класс устанавливающий курсор ожидания и автоматически сбрасывающий его

Рейтинг: 3Ответов: 2Опубликовано: 09.04.2015

Создал вот такой класс:

class CursorEx: IDisposable
{
    public CursorEx()
    {
        Cursor.Current = Cursors.WaitCursor;
    }
    public void Dispose()
    {
        Cursor.Current = Cursors.Default;
    }
}

Использую вот так:

public void Method()
{
    new CursorEx();
    //Здаесь что-то выполняется
}

Подразумевается, что после выполнения метода, вызовется метод Dispose() и курсор установится на дефолтный. Могут ли быть какие-то варианты, когда произойдет сбой?

Ответы

▲ 3Принят

Во-первых, вы забыли вызвать Dispose. Для этого следует использовать using:

using (new CursorEx()) {
    // что-то выполняется
}

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

Во-вторых, вы не восстанавливаете исходный курсор. Если какая-то функция изменяет курсор, а затем вызывает функцию, которая тоже изменяет курсор, то курсор будет восстановлен уже после возврата из первой функции:

using (new CursorEx()) {
    using (new CursorEx()) {
        // что-то выполняется
    }
    // курсор уже восстановлен
}
// курсор должен был быть восстановлен здесь

Вам следует сохранять текущий курсор в конструкторе и восстанавливать его в Dispose.

В-третьих, можно избавиться от лишнего объекта в памяти, если заменить класс на структуру.

Итого:

public struct CursorWaiter : IDisposable
{
    private readonly Cursor _originalCursor;

    public CursorWaiter (Cursor newCursor = null)
    {
        _originalCursor = Cursor.Current;
        Cursor.Current = newCursor ?? Cursors.WaitCursor;
    }

    public void Dispose ()
    {
        Cursor.Current = _originalCursor;
    }
}

Из комментариев: этот код рассчитан на то, что программист будет использовать тип только с помощью using, не будет уничтожать объект по несколько раз, не будет копировать и т.п. Если есть сомнения в этом, то нужно добавить проверку на повторный вызов Dispose (с помощью отдельного флага или обнуления _originalCursor), а также заменить класс на структуру для избавления он возможности копирования или вовсе скрыть тип за методом с лямбдой.

▲ 2

Конечно. Это вообще хоть раз сработало? Вы неверно пользуетесь сборкой мусора, .NET работает сосвсем не так, как C++.

Объект будет уничтожен не в конце метода, а когда захочется сборщику мусора. Поскольку у вас нет на него ссылок, то он может вообще быть уничтожен сразу после создания. А метод Dispose вообще не будет вызван автоматически, IDisposable для рантайма никакого специального значения не имеет.

Делайте так:

public void Method()
{
    using (new CursorEx())
    {
        //Здаесь что-то выполняется
    }
}

И обязательно разберитесь со временем жизни объектов в .NET, без этого никуда.