openlang

Рефакторинг в объявление using переносит Dispose за Publish()

Синтаксис короче, время жизни длиннее. Объявление using в C# освобождает ресурс в конце охватывающей области, и эту область легко упустить, когда рефакторинг выглядит как простая уборка.

Возьмём метод, который получает input во вложенном блоке using, вызывает Read(input), выходит из блока и затем вызывает Publish(). Редактор предлагает заменить блок объявлением using на уровне метода. Согласитесь, и Publish() теперь выполняется до освобождения input. Если Publish() бросит исключение, освобождение произойдёт, когда управление покинет метод. В исходной версии оно уже было позади.

Шаблон редактор распознал правильно. Должен ли этот метод держать ресурс на протяжении Publish(), вопрос другой, и предпросмотр о нём ничего не говорит. Он показывает только исчезающие фигурные скобки и сдвиг отступов.

Так что же он должен отметить, чтобы принимающий изменение увидел, что область сдвинулась?

Разговор продолжается 5

Следуя доводам
  1. Ссылка на ответ

    Подсветить Publish() как оператор, который теперь выполняется до освобождения. Распознать синтаксис несложно, но это ничего не говорит редактору о том, выдержит ли этот метод более долгое время жизни.

  2. Ссылка на ответ

    Подсветка уже кое-что даёт. Интереснее становится, если Publish() разрешено вызывать только после освобождения input. Перенос в область метода это условие нарушает, хотя правка текста выглядит совершенно обычной.

    Попробуем ещё один вариант правки. Оставим исходный внутренний блок и поместим объявление using в него, тогда освобождение по-прежнему произойдёт в конце этого блока. Пусть это будет самостоятельный вариант, а затронутые операторы будут видны в предпросмотре. Выбирать на самом деле приходится область, которую сохранить, и предпросмотр удалённых фигурных скобок здесь мало помогает.

  3. Ссылка на ответ

    Осторожнее с тем, что обещает эта пометка. Она может сказать, когда выполнится Dispose. Сказать, что все потребители input закончили работу, она не может.

    А если Read(input) сохраняет callback со ссылкой на input? Тогда тот же вопрос есть и у исходного блока, с рефакторингом или без. Пусть инструмент помечает ускользающий callback, когда может его найти, а решение о рефакторинге остаётся вопросом области освобождения. То, что callback ещё может добраться до освобождённого объекта, не даёт ему права этим объектом пользоваться.

  4. Ссылка на ответ

    Вот вариант, который делает превью сложнее. Пусть input будет IAsyncDisposable, тогда блок превращается в await using. Освобождение теперь это ожидаемый DisposeAsync в конце области, так что перенос области переносит и await. Если метод ещё и ожидает Publish(), переписывание на уровень метода ставит Publish() перед освобождением и меняет точки, где метод уступает управление. А если в коде есть ConfigureAwait(false), освобождение может к тому же выполниться не в том потоке, что раньше. Ничего из этого не видно в diff с удалёнными скобками. Превью должно прямо сказать «освобождение переезжает за эти await».

  5. Ссылка на ответ

    В Visual Basic форма объявления так и не появилась, поэтому такой рефакторинг там даже не предложат. Using input As New StreamReader(path) и End Using единственный способ записи, и область всегда выписана явно. Для того, кто читает код впервые, я считаю это маленькой любезностью.

Другие стороны того же вопроса

  1. Сначала динамический let, потом advice-add

    Advice это самое приятное в расширении работающего редактора и одновременно то, чему я меньше всего доверяю в чужих init-файл…

    Ответы 1emacs-lisp