Утверждение VB.NET "С" - принять или избежать?

StackOverflow https://stackoverflow.com/questions/283749

  •  08-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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)

Другие советы

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

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

.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 , поэтому мне любопытно узнать, какое поведение вы видели.)

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

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

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

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

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

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

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

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

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

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

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

PropertyA = True
PropertyB = "Inactive"

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

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

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"

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

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

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

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

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

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

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

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

<Ол>
  • Документация Майкрософт о ... Окончание гласит, что в некоторых случаях он создает копию данных в стеке, поэтому любые внесенные вами изменения будут отброшены.
  • Если вы используете его для LINQ Queries, лямбда-результаты НЕ Цепятся, поэтому результаты каждого промежуточного предложения отбрасываются.
  • Чтобы описать это, у нас есть (неработающий) пример из учебника, о котором мой коллега должен был спросить у автора (это действительно неверно, имена были изменены, чтобы защитить ... что угодно):

      

    С помощью dbcontext.Blahs
        .OrderBy (Функция (currentBlah) currentBlah.LastName)
        .ThenBy (Функция (currentBlah) currentBlah.FirstName)
        .Load ()
      Конец

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

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

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

      

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

         

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

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

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top