Использование 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

Нет. Вы понижаете рейтинг ключевого слова на языке [что означает, что компилятор может делать что-то вокруг него, как указал Флориан], на обычный вызов метода.

Хотя семантически они могут быть одинаковыми, компилятор не знает, что будет делать этот метод. Проверка времени компиляции лучше, чем исключения времени выполнения, как мы все знаем.