VB.NET 'With' Statement - обнимать или избегать?

На работе я часто работаю над проектами, в которых во время их строительства или в начале их жизни необходимо устанавливать многочисленные свойства определенных объектов. Для удобства и удобочитаемости я часто использую оператор With для установки этих свойств. Я считаю, что

With Me.Elements
    .PropertyA = True
    .PropertyB = "Inactive"
    ' And so on for several more lines
End With

Выглядит намного лучше, чем

Me.Elements.PropertyA = True
Me.Elements.PropertyB = "Inactive"
' And so on for several more lines

для очень длинных операторов, которые просто устанавливают свойства.

Я заметил, что есть некоторые проблемы с использованием With во время отладки; однако Мне было интересно, были ли какие-либо веские причины, чтобы избежать использования With на практике? Я всегда предполагал, что код, сгенерированный с помощью компилятора для вышеупомянутых двух случаев, в основном тот же, поэтому я всегда предпочитаю писать то, что, по моему мнению, более читабельным.

Ответ 1

Если у вас есть длинные переменные имена и в итоге получится:

UserHandler.GetUser.First.User.FirstName="Stefan"
UserHandler.GetUser.First.User.LastName="Karlsson"
UserHandler.GetUser.First.User.Age="39"
UserHandler.GetUser.First.User.Sex="Male"
UserHandler.GetUser.First.User.Occupation="Programmer"
UserHandler.GetUser.First.User.UserID="0"
....and so on

то я бы использовал WITH, чтобы сделать его более читаемым:

With UserHandler.GetUser.First.User
    .FirstName="Stefan"
    .LastName="Karlsson"
    .Age="39"
    .Sex="Male"
    .Occupation="Programmer"
    .UserID="0"
end with

В более позднем примере есть даже преимущество в производительности по сравнению с первым примером, потому что в первом примере Im выбирает пользователя каждый раз, когда я обращаюсь к пользовательскому свойству, а в случае WITH я только выбираю пользователя один раз.

Я могу получить прирост производительности без использования, например:

dim myuser as user =UserHandler.GetUser.First.User
myuser.FirstName="Stefan"
myuser.LastName="Karlsson"
myuser.Age="39"
myuser.Sex="Male"
myuser.Occupation="Programmer"
myuser.UserID="0"

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

И я просто взял это как пример, поэтому не жалуюсь на класс со многими ключевыми словами, другой пример может быть похож: WITH RefundDialog.RefundDatagridView.SelectedRows(0)

Ответ 2

На практике у него нет действительно серьезных аргументов. Я не поклонник, но это личное предпочтение, нет эмпирических данных, чтобы предположить, что конструкция With плохая.

В .NET он компилируется точно так же, как и полностью квалифицирует имя объекта, поэтому для этого сахара не наблюдается снижения производительности. Я установил это, компилируя, затем дизассемблируя следующий класс VB.NET 2.0:

Imports System.Text

Public Class Class1
    Public Sub Foo()
        Dim sb As New StringBuilder
        With sb
            .Append("foo")
            .Append("bar")
            .Append("zap")
        End With

        Dim sb2 As New StringBuilder
        sb2.Append("foo")
        sb2.Append("bar")
        sb2.Append("zap")
    End Sub
End Class

Разборка выполняется следующим образом: обратите внимание, что вызовы метода sb2 Append выглядят идентично оператору With для sb:

.method public instance void  Foo() cil managed
{
  // Code size       91 (0x5b)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Text.StringBuilder sb,
           [1] class [mscorlib]System.Text.StringBuilder sb2,
           [2] class [mscorlib]System.Text.StringBuilder VB$t_ref$L0)
  IL_0000:  nop
  IL_0001:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  stloc.2
  IL_0009:  ldloc.2
  IL_000a:  ldstr      "foo"
  IL_000f:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0014:  pop
  IL_0015:  ldloc.2
  IL_0016:  ldstr      "bar"
  IL_001b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0020:  pop
  IL_0021:  ldloc.2
  IL_0022:  ldstr      "zap"
  IL_0027:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_002c:  pop
  IL_002d:  ldnull
  IL_002e:  stloc.2
  IL_002f:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0034:  stloc.1
  IL_0035:  ldloc.1
  IL_0036:  ldstr      "foo"
  IL_003b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0040:  pop
  IL_0041:  ldloc.1
  IL_0042:  ldstr      "bar"
  IL_0047:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_004c:  pop
  IL_004d:  ldloc.1
  IL_004e:  ldstr      "zap"
  IL_0053:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0058:  pop
  IL_0059:  nop
  IL_005a:  ret
} // end of method Class1::Foo

Итак, если вам это нравится, и найти его более читаемым, пойдите для него; нет никакой веской причины не делать этого.

(Кстати, Tom, мне интересно узнать, что произошло с отладчиком - я не могу вспомнить, когда-либо наблюдал какое-либо необычное поведение в отладчик на основе инструкции With, поэтому мне интересно узнать, какое поведение вы видели.)

Ответ 3

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

Когда используется оператор WITH, он создает новую локальную переменную, ссылающуюся на объект. Последующие ссылки, использующие .xx, являются ссылками на свойства этой локальной ссылки. Если во время выполнения оператора WITH исходная ссылка на переменную изменяется, объект, на который ссылается WITH, не изменяется. Рассмотрим:

Dim AA As AAClass = GetNextAAObject()
With AA
    AA = GetNextAAObject()

    '// Setting property of original AA instance, not later instance
    .SomeProperty = SomeValue
End With

Итак, оператор WITH - это не просто синтаксический сахар, это действительно другая конструкция. Хотя вы вряд ли будете кодировать что-то явно, как указано выше, в некоторых ситуациях это может произойти непреднамеренно, поэтому вы должны знать об этой проблеме. Наиболее вероятная ситуация заключается в том, что вы можете перемещаться по такой структуре, как сеть объектов, межсоединения которых меня неявно изменяются путем установки свойств.

Ответ 4

Все об удобочитаемости. Как и весь синтаксический сахар, он может быть чрезмерно использован.

Обнять его, если вы устанавливаете несколько элементов объекта в нескольких строках

With myObject
  .Property1 = arg1
  .Property2 = arg2
...

Избегайте делать что-либо еще с помощью "С"

Если вы пишете блок С, который охватывает 50-100 строк и включает в себя множество других переменных, это может сделать ДЕЙСТВИТЕЛЬНО трудно запомнить то, что было объявлено в верхней части блока. По очевидным причинам я не буду приводить пример такого грязного кода

Ответ 5

Где он делает код действительно более читаемым, пойдите для него. Там, где это делает его менее читаемым, избегайте его - в частности, я предлагаю избегать вложений с операторами.

С# 3.0 имеет эту функцию исключительно для инициализации объекта:

var x = new Whatever { PropertyA=true, PropertyB="Inactive" };

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

Одна заметка о вашем примере - вам действительно нужно "Я" вообще? Почему бы просто не написать:

PropertyA = True
PropertyB = "Inactive"

? Разумеется, в этом случае подразумевается "Я"...

Ответ 6

Я бы с подозрением относился к коду, который использует много этого ключевого слова: если он используется для упрощения установки множества переменных экземпляра или свойств, я думаю, это может указывать на то, что ваши классы слишком велики (Запах большого класса). Если вы используете его для замены длинных цепочек вызовов, например:

UserHandler.GetUser.First.User.FirstName="Stefan"
UserHandler.GetUser.First.User.LastName="Karlsson"
UserHandler.GetUser.First.User.Age="39"
UserHandler.GetUser.First.User.Sex="Male"
UserHandler.GetUser.First.User.Occupation="Programmer"
UserHandler.GetUser.First.User.UserID="0"

то вы, вероятно, нарушите Закон о Демерете

Ответ 7

Я не использую VB.NET(я использовал обыкновенный VB), но...

Обязательна ли ведущая точка? Если да, то я не вижу проблемы. В Javascript результат использования with заключается в том, что свойство объекта выглядит так же, как и простая переменная, и это очень опасно, поскольку вы не видите, имеете ли вы доступ к свойству или переменной, и таким образом, with следует избегать.

Не только его использование проще на глазах, но и для повторного доступа к свойствам объекта, оно, вероятно, будет быстрее, поскольку объект извлекается через цепочку методов только один раз, а не один раз для каждого свойства.

Я согласен с другими ответами, что вам следует избегать вложенного использования with по той же причине, что и для того, чтобы избежать with вообще в Javascript: потому что вы больше не видите, к какому объекту принадлежит ваше свойство.

Ответ 8

"with" - это в основном "каскад" от Smalltalk. Это образец в книге Кента Бек Smalltalk Best Practice Patterns.

Резюме шаблона: используйте его, когда имеет смысл группировать сообщения, отправленные объекту. Не используйте его, если это просто сообщения, отправленные на один и тот же объект.

Ответ 9

ИЗБЕГАЙТЕ блок WITH любой ценой (даже читаемость). Две причины:

  • Документация Microsoft о с... End With говорит, что в некоторых случаях она создает копию данных в стеке, поэтому любые сделанные вами изменения будут выброшены.
  • Если вы используете его для запросов LINQ, результаты лямбда не замыкаются, поэтому каждый результат промежуточной оговорки отбрасывается.

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

С dbcontext.Blahs
  .OrderBy(Function (currentBlah) currentBlah.LastName)
  .ThenBy(Функция (currentBlah) currentBlah.FirstName)
  .load()
Конец с

У OrderBy и ThenBy есть Без эффекта. Если вы переформатируете код, ТОЛЬКО отбрасывая С и заканчивая С, и добавляя символы продолжения строки в конце первых трех строк... он работает (как показано на 15 страницах позже в том же учебнике).

Нам больше не нужно искать и уничтожать WITH Blocks. Они имели смысл только в Интерпретированной структуре.

Ответ 10

Там, где вы используете его со структурой, возникает вопрос: иначе вы не можете устанавливать свои поля, так как вы работаете над локальной копией (сделанной во время входа в блок) выражения "с" и не работая с (копия) ссылки объекта в этом случае:

Тип данных объекта Expression может быть любым типом класса или структуры или даже элементарный тип Visual Basic, такой как Integer. Если objectExpression приводит к чему-либо, кроме объекта, вы можете только читать значения своих членов или ссылаться на методы, и вы получаете если вы попытаетесь присвоить значения членам структуры, используемой в С... End С выражением. Это та же ошибка, которую вы получили бы, если бы вызывается метод, который возвращает структуру и сразу же получает доступ и присваивает значение члену результата функции, например GetAPoint(). X = 1. Проблема в обоих случаях заключается в том, что структура существует только в стеке вызовов, и нет способа изменить член структуры в этих ситуациях может записывать в такое местоположение, что любой другой код в программе может наблюдать изменение.

Объектное выражение оценивается один раз, после входа в блок. Вы не может переназначить выражение объекта из блока With.

https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/with-end-with-statement

Угадайте, что компилятор мог быть немного более умным, если вы передадите с выражением имя структуры вместо выражения, которое возвращает структуру, но похоже, что это не

Ответ 11

Я уверен, что это только VB.net, но я могу ошибаться. Будучи парнем С#, я всегда немного завидовал этому синтаксическому сахару.