Плохая практика? Неканоническое использование оператора С# 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
для непреднамеренных целей? Другими словами, совместимо ли это с перегрузкой оператора неинтуитивным способом?
Ответы
Ответ 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");