Использование vs lambda
Это эквивалентно?
public static void Using<T>(this T disposable, Action<T> action)
where T:IDisposable
{
try {
action(disposable);
}
finally {
disposable.Dispose();
}
}
new SqlConnection("").Using(conn => {
});
using(var conn = new SqlConnection("")){
};
Другими словами, можно ли заменить ключевое слово этим методом?
Ответы
Ответ 1
Я не думаю, что это особенно хорошая идея, потому что это также позволило бы нам написать
var conn = new SqlConnecction("");
conn.Using(c => /* do something with c */);
conn.Open();
Это будет скомпилировано, но когда вы дойдете до последней строки, это вызовет ObjectDisposedException.
Во всяком случае, идиома кодирования using
хорошо известна, поэтому почему ваши коллеги-разработчики усложняют чтение вашего кода?
Ответ 2
Я отмечаю, что ваш метод не делает все, что делает инструкция "using". Например, он не вводит новое пространство объявления локальной переменной, в котором может быть объявлена переменная, которая хранится на ресурсе.
Также он не позволяет объявлять несколько одноразовых ресурсов того же типа за один раз.
И, наконец, он не кажется особенно элегантным с самим собой. Одна из приятных вещей о "использовании" в качестве утверждения заключается в том, что вы можете сказать:
using(something)
using(someotherthing)
using(somethirdthing)
{
use all three, they'll all get disposed
}
Как это будет выглядеть в вашем предложении?
something.Using(
x=>someotherthing.Using(
y=>somethirdthing.Using(
z=> { use all three } )));
Немного грубо, честно говоря.
Ответ 3
Одно ограничение: вы не можете получить параметры содержащего метода в своей лямбда.
Ответ 4
Оператор using
проверяет наличие disposable
null
. Помимо этого во время выполнения вы получите в основном то же поведение (кроме дополнительного фрейма стека и вызов делегата).
Ответ 5
Некоторые причины, по которым я не хочу этого использовать.
Защита от вредоносных программ
using (var foo=new Bar())
{
foo=new Bar();// throws an error at compile time
}
Структуры, реализующие IDisposable
Структуры, реализующие интерфейсы, помещаются в бокс, когда на них вызывается метод интерфейса.
Оператор using достаточно умен, чтобы узнать, когда его цель является структурой во время компиляции и отказывается от бокса.
Ответ 6
Как сказал Ричард, есть дополнительный вызов метода между созданием одноразового объекта и началом попытки. Это увеличивает временной интервал, в котором может происходить асинхронное исключение, что приводит к тому, что ваш одноразовый объект не будет удален. Однако это не большая проблема, потому что типы, которые реализуют IDisposable, не должны приобретать свои ресурсы в конструкторе (SqlConnection сохраняет свои ресурсы во время вызова метода Open()).
Итак, пока ваш новый способ написания оператора using будет работать, он значительно затрудняет чтение для других разработчиков и может легко подвергнуться злоупотреблениям, как отмечает Марк Семанн.
Итак, можете ли вы заменить ключевое слово using с помощью метода использования расширения? Да. Должны ли вы это сделать? Нет.
Ответ 7
Это почти функционально эквивалент, но практически говорящий он не дает никакой пользы. Семантика "традиционного" выражения using
хорошо известна и стандартная. Хотя ваш подход может вести себя одинаково (хотя есть некоторые различия), он вводит ненужные накладные расходы и сложность - вы не только получаете дополнительные вызовы методов, которые вы сами копируете код, который автоматически генерирует компилятор.
Ответ 8
Нет.
Вы понижаете рейтинг ключевого слова на языке [что означает, что компилятор может делать что-то вокруг него, как указал Флориан], на обычный вызов метода.
Хотя семантически они могут быть одинаковыми, компилятор не знает, что будет делать этот метод.
Проверка времени компиляции лучше, чем исключения времени выполнения, как мы все знаем.