Утверждение VB.NET "С" - принять или избежать?
-
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 любой ценой (даже читабельность). Две причины:
<Ол>Чтобы описать это, у нас есть (неработающий) пример из учебника, о котором мой коллега должен был спросить у автора (это действительно неверно, имена были изменены, чтобы защитить ... что угодно):
С помощью 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.
думаю, что компилятор мог бы быть немного умнее, если вы передаете с помощью Statement имя структуры вместо выражения, которое возвращает структуру, но, похоже, это не так