Подтвердить что ты не робот

Плохая практика? Неканоническое использование оператора С# using

С# имеет оператор using, особенно для объектов IDisposable. Предположительно, любой объект, указанный в операторе using, будет содержать определенный ресурс, который должен быть освобожден детерминированным образом.

Однако мне кажется, что в программировании есть много проектов, которые имеют одно, определенное начало и конец, но не обладают внутренней поддержкой языка. Конструкция using предоставляет возможность использовать встроенные функции редактора кода, чтобы, по крайней мере, четко и естественно выделить область такой конструкции или операции.

То, что я имею в виду, - это такая операция, которая часто начинается с метода BeginXXX() и EndXXX(), хотя существует множество различных вариантов, таких как выполнение асинхронного кода, которое включает в себя "начало" и "старт", присоединиться".

Возьмите этот наивный пример.

webDataOperation.Start();
GetContentFromHardDrive();
webDataOperation.Join();
// Perform operation that requires data from both sources

Что, если вместо этого метод Start возвратил объект, метод IDisposable.Dispose выполняет операцию соединения.

using(webDataOperation.Start()) {
    GetContentFromHardDrive();
}
// Perform operation that requires data from both sources

Или, еще лучше, что я специально имел в виду: у меня есть объект, который выполняет высокоспециализированную графическую бликировку и имеет метод Begin() и End() (дизайн также присутствует в DirectX и XNA). Вместо того, чтобы...

using(blitter.BlitOperation()) {
    // Do work
}
// Use result

Это кажется более естественным и читаемым, но нецелесообразно, поскольку он использует интерфейс IDisposable и оператор using для непреднамеренных целей? Другими словами, совместимо ли это с перегрузкой оператора неинтуитивным способом?

4b9b3361

Ответ 1

Это вполне приемлемая практика. Они называются факторизованными типами, а Framework Design Guidelines рекомендует делать именно это.

В принципе, если тип обертывает операцию с определенным временем жизни, использование IDisposable и оператора using становится подходящей вещью для рассмотрения.

Я действительно писал о этой конкретной теме здесь.

Ответ 2

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

Я в меньшинстве. Большинство людей, похоже, используют "использование" в качестве общей цели "Я хочу, чтобы какой-то код очистки запускался, даже если исключение было брошено".

Мне это не нравится, потому что (1) у нас уже есть механизм для этого, называемый "try-finally", (2) он использует функцию для цели, для которой она не была предназначена, и (3) если вызов код очистки важен, то почему он не отображается в точке, где он называется? Если это важно, я хочу видеть его.

Ответ 3

Просто потому, что вы можете (или потому, что Phil Haack говорит, что все в порядке), это не значит, что вам нужно.

Основное правило: если я могу читать ваш код и понимать, что он делает и каковы ваши намерения, тогда это приемлемо. Если, с другой стороны, вам нужно объяснить, что вы сделали, или почему вы это сделали, вероятно, это приведет к отключению младших разработчиков, поддерживающих код.

Есть много других шаблонов, которые могут выполнить это с лучшей инкапсуляцией.

В нижней строке: эта "техника" ничего не покупает и только пугает других разработчиков.

Ответ 4

Это обычная картина, но лично я считаю, что нет оправдания злоупотреблять подобным образом, когда вы можете добиться такого же эффекта гораздо более очевидным образом с анонимными делегатами и/или lambdas; то есть:.

blitter.BlitOperation(delegate
{
   // your code
});

Ответ 5

Я думаю, вы должны использовать IDisposable для того, для чего он предназначен, и ничего больше. То есть, если для вас важна ремонтопригодность.

Ответ 6

Я бы сказал, что это приемлемо - на самом деле, я использовал его в некоторых проектах, где я хотел, чтобы действие было инициировано в конце определенного блока кода.

Wes Deyer использовал его в своей программе LINQ to ASCII Art, он назвал его действием одноразовым (Wes работает в команде компилятора С# - я бы доверял его мнению: D):

http://blogs.msdn.com/wesdyer/archive/2007/02/23/linq-to-ascii-art.aspx

class ActionDisposable: IDisposable
{
    Action action;

    public ActionDisposable(Action action)
    {
        this.action = action;
    }

    #region IDisposable Members

    public void Dispose()
    {
        this.action();
    }

    #endregion
}

Теперь вы можете вернуть это из функции и сделать что-то вроде этого:

using(ExtendedConsoleWriter.Indent())
{
     ExtendedConsoleWriter.Write("This is more indented");
}

ExtendedConsoleWriter.Write("This is less indented");