До сих пор я думал, что этого не было (глупо от меня, я знаю...) и делал вот так:
Есть ли какие-либо последствия для лучшей практики? Должен ли я перейти и переписать весь код, используя With New MyDisposableObject() ... End With
как Using o as New MyDisposableObject()
?
Ответ 2
With
Заявления/Блокировки
Однако, насколько я понимаю, конструкция With New... будет иметь объект, помеченный как объект, готовый для сбора мусора, когда он выходит за рамки.
Это верно и не верно. Это верно в том смысле, что все объекты "отмечены" (пуристы могут спорить с этой терминологией, но детали не актуальны), как готовые к сборке мусора, когда они выходят за рамки. Но тогда в этом смысле это также не совсем так, поскольку в отношении этого поведения нет особого ключевого слова With
. Когда объект выходит из сферы действия, он имеет право на сбор мусора. Период. Это верно для области уровня метода, и это верно для области уровня блока (например, With
, For
, Using
и т.д.).
Но не потому, что вы используете With
. Причина в том, что он позволяет вам устанавливать несколько свойств последовательно на глубоко вложенном объекте. Другими словами, предположим, что у вас есть объект, на котором вы хотите установить кучу свойств, и вы получите доступ к нему следующим образом: MyClass.MemberClass.AnotherMemberClass.Items(0)
. Посмотреть все эти точки? Он может (теоретически) стать неэффективным для написания кода, который должен работать через эту последовательность точек снова и снова, чтобы получить доступ к одному и тому же объекту каждый раз, когда вы устанавливаете на нем свойство. Если вы знаете что-либо о C или С++ (или любом другом языке с указателями), вы можете думать о каждой из этих точек, что подразумевает разыменование указателя. Оператор With
в основном проходит через всю эту косвенность только один раз, сохраняя результирующий объект во временной переменной и позволяя вам устанавливать свойства непосредственно на этот объект, хранящийся в временная переменная.
Возможно, какой-то код поможет сделать вещи более ясными. Всякий раз, когда вы видите точку, подумайте, что это может быть медленным!
Предположим, что вы начинаете со следующего кода, извлекаете объект 1 из глубоко вложенной коллекции Items
и устанавливаете на нем несколько свойств. Посмотрите, сколько раз нам нужно извлекать объект, даже если он точно тот же самый объект каждый раз?
MyClass.MemberClass.AnotherMemberClass.Items(0).Color = Blue
MyClass.MemberClass.AnotherMemberClass.Items(0).Width = 10
MyClass.MemberClass.AnotherMemberClass.Items(0).Height = 5
MyClass.MemberClass.AnotherMemberClass.Items(0).Shape = Circle
MyClass.MemberClass.AnotherMemberClass.Items(0).Texture = Shiny
MyClass.MemberClass.AnotherMemberClass.Items(0).Volume = Loud
Теперь мы модифицируем этот код для использования блока With
:
With MyClass.MemberClass.AnotherMemberClass.Items(0)
.Color = Blue
.Width = 10
.Height = 5
.Shape = Circle
.Texture = Shiny
.Volume = Loud
End With
Эффект здесь, однако, идентичен следующему коду:
Dim tempObj As MyObject = MyClass.MemberClass.AnotherMemberClass.Items(0)
tempObj.Color = Blue
tempObj.Width = 10
tempObj.Height = 5
tempObj.Shape = Circle
tempObj.Texture = Shiny
tempObj.Volume = Loud
Конечно, вы не вводите новую область видимости, поэтому tempObj
не выйдет за рамки (и, следовательно, будет иметь право на сбор мусора) до тех пор, пока не исчезнет область более высокого уровня, но это вряд ли будет иметь значение. Коэффициент усиления производительности (если таковой имеется) присоединяется к обоим последним двум фрагментам кода.
Настоящая победа в использовании блоков With
в настоящее время - не производительность, а читаемость. Дополнительные мысли о With
, возможные улучшения производительности, стилистические предложения и т.д. См. ответы на этот вопрос.
With New
?
Добавление ключевого слова New
в оператор With
имеет тот же эффект, о котором мы только что говорили (создание локальной временной переменной для хранения объекта), за исключением того, что оно почти совершенно бессмысленно. Если вам нужно создать объект с помощью New
, вы можете также объявить переменную для ее хранения. Вероятно, вам придется что-то сделать с этим объектом позже, например передать его другому методу, и вы не можете сделать это в блоке With
.
Кажется, единственной целью With New
является то, что она позволяет избежать явного объявления переменной, вместо этого заставить компилятор делать это неявно. Назовите меня сумасшедшим, но я не вижу в этом никакого преимущества.
Фактически, я могу сказать, что я честно никогда не видел никакого реального кода, который использует этот синтаксис. Единственное, что я могу найти в Google, - это глупость, подобная этому (и Call
- гораздо лучшая альтернатива там, в любом случае).
Using
Заявления/Блокировки
В отличие от With
, Using
невероятно полезен и должен часто появляться в типичном коде VB.NET. Однако он очень ограничен в своей применимости: он работает только с объектами, тип которых реализует шаблон интерфейса IDisposable
. Вы можете сказать это, проверив, имеет ли объект метод Dispose
, который вы должны вызывать, когда вы закончите с ним, чтобы освободить неуправляемые ресурсы.
Это, кстати, правило, которое вы всегда должны соблюдать, когда объект имеет метод Dispose
: вы всегда должны вызывать его всякий раз, когда вы закончите использовать этот объект. Если вы этого не сделаете, это не обязательно конец света - сборщик мусора может спасти ваш бекон, но это часть документально подтвержденного контракта и всегда хорошая практика с вашей стороны вызывать Dispose
для каждого объекта, который его предоставляет.
Если вы попытаетесь завернуть создание объекта, который не реализует IDisposable
в блоке Using
, компилятор будет лаять на вас и генерировать ошибку. Это не имеет смысла для других типов, потому что его функция по существу эквивалентна блоку Try
/Finally
:
Try
' [1: Create/acquire the object]
Dim g As Graphics = myForm.CreateGraphics()
' [2: Use the object]
g.DrawLine(Pens.Blue, 10, 10, 100, 100)
' ... etc.
End Try
Finally
' [3: Ensure that the object gets disposed, no matter what!]
g.Dispose()
End Finally
Но это уродливо и становится довольно громоздким, когда вы начинаете вложенность (например, если бы мы создали объект Pen
, который также должен был быть удален). Вместо этого мы используем Using
, который имеет тот же эффект:
' [1: Create/acquire the object]
Using g As Graphics = myForm.CreateGraphics()
' [2: Use the object]
g.DrawLine(Pens.Blue, 10, 10, 100, 100)
' ...etc.
End Using ' [3: Ensure that the object gets disposed, no matter what!]
Оператор Using
работает как с объектами, которые вы сначала приобретаете (используя ключевое слово New
, либо вызывая метод типа CreateGraphics
), и с уже созданными вами объектами. В обоих случаях он гарантирует, что метод Dispose
вызывается, даже если исключение выбрасывается где-то внутри блока, что гарантирует правильное расположение объектов неуправляемых ресурсов.
Меня немного пугает, что вы написали код в VB.NET, не зная о инструкции Using
. Вы не используете его для создания всех объектов, но это очень важно, когда вы имеете дело с объектами, которые реализуют IDisposable
. Вам обязательно нужно вернуться и повторно проверить свой код, чтобы убедиться, что вы используете его там, где это необходимо!