Заявление VB.NET «С» - принять или избежать? - PullRequest
67 голосов
/ 12 ноября 2008

На работе я часто работаю над проектами, в которых многочисленные свойства определенных объектов должны быть установлены во время их строительства или в начале их жизни. Для удобства и удобства чтения я часто использую оператор 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 на практике ? Я всегда предполагал, что код, сгенерированный с помощью компилятора для двух вышеупомянутых случаев, в основном один и тот же, поэтому я всегда выбирал писать то, что я считаю более читабельным.

Ответы [ 10 ]

64 голосов
/ 12 ноября 2008

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

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

В последнем примере есть даже выигрыш в производительности по сравнению с первым, потому что в первом примере я выбираю пользователя каждый раз, когда я получаю доступ к свойству пользователя, а в случае 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)

24 голосов
/ 12 ноября 2008

На практике нет действительно убедительных аргументов против этого. Я не фанат, но это личное предпочтение, нет эмпирических данных, позволяющих предположить, что конструкция 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

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

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

15 голосов
/ 17 августа 2013

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

При использовании оператора 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 - это не просто синтаксический сахар, это действительно другая конструкция. Хотя вам вряд ли удастся закодировать что-либо явное, как указано выше, в некоторых ситуациях это может произойти непреднамеренно, поэтому вам следует знать об этой проблеме. Наиболее вероятна ситуация, когда вы можете проходить через такую ​​структуру, как сеть объектов, чьи взаимосвязи могут быть неявно изменены путем установки свойств.

11 голосов
/ 05 апреля 2012

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

Примите его, ЕСЛИ вы устанавливаете несколько элементов объекта в несколько строк

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

Избегайте делать что-либо еще с «С»

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

6 голосов
/ 12 ноября 2008

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

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

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

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

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

PropertyA = True
PropertyB = "Inactive"

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

5 голосов
/ 12 ноября 2008

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

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"

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

3 голосов
/ 12 ноября 2009

«С» - это в основном «каскад» из Smalltalk. Это образец из книги «Образцы лучших практик Smalltalk» Кента Бека.

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

3 голосов
/ 12 ноября 2008

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

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

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

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

2 голосов
/ 24 сентября 2015

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

  1. Документация Microsoft о With ... Окончание говорит, что при некоторых обстоятельствах он создает копию данных в стеке, поэтому любые внесенные вами изменения будут отбрасываться.
  2. Если вы используете его для LINQ Queries, лямбда-результаты НЕ Цепятся, и поэтому результаты каждого промежуточного предложения отбрасываются.

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

С dbcontext.Blahs
.OrderBy (Функция (currentBlah) currentBlah.LastName)
.ThenBy (Функция (currentBlah) currentBlah.FirstName)
.Load () * +1021 * Конец

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

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

1 голос
/ 17 августа 2017

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

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

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

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

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

...