VB.NET'With'声明 - 拥抱还是避免?

Tom*_*Tom 70 vb.net with-statement

在工作中,我经常在一些项目中工作,这些项​​目必须在建造期间或生命早期设置某些物体的许多属性.为了方便和可读性,我经常使用该With语句来设置这些属性.我发现

With Me.Elements
    .PropertyA = True
    .PropertyB = "Inactive"
    ' And so on for several more lines
End With
Run Code Online (Sandbox Code Playgroud)

看起来好多了

Me.Elements.PropertyA = True
Me.Elements.PropertyB = "Inactive"
' And so on for several more lines
Run Code Online (Sandbox Code Playgroud)

对于只设置属性的很长的语句.

我注意到With在调试时使用时存在一些问题; 但是,我想知道是否有任何令人信服的理由避免With在实践中使用?我总是假设通过编译器为上述两种情况生成的代码基本相同,这就是为什么我总是选择写出我觉得更具可读性的原因.

Ste*_*fan 66

如果你有很长的变量名,最终会得到:

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
Run Code Online (Sandbox Code Playgroud)

那么我会使用WITH来使它更具可读性:

With UserHandler.GetUser.First.User
    .FirstName="Stefan"
    .LastName="Karlsson"
    .Age="39"
    .Sex="Male"
    .Occupation="Programmer"
    .UserID="0"
end with
Run Code Online (Sandbox Code Playgroud)

在后面的示例中,与第一个示例相比,甚至具有性能优势,因为在第一个示例中,每次访问用户属性时都会获取用户,而在WITH-case中,我只获取用户一次.

我可以在不使用的情况下获得性能提升,如下所示:

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"
Run Code Online (Sandbox Code Playgroud)

但我会选择WITH语句,它看起来更干净.

我只是以此为例,所以不要抱怨有很多关键字的类,另一个例子可能是:WITH RefundDialog.RefundDatagridView.SelectedRows(0)

  • +1也用于解释性能差异.建议:在答案的顶部放置**以粗体显示**. (6认同)
  • 在大多数情况下,编译器是否不会优化这种性能增益? (2认同)

Joh*_*udy 24

在实践中,没有真正令人信服的观点.我不是粉丝,但这是个人偏好,没有经验数据表明这种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
Run Code Online (Sandbox Code Playgroud)

拆卸如下-请注意,在调用sb2Append方法看起来相同的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
Run Code Online (Sandbox Code Playgroud)

所以如果你喜欢它,并发现它更具可读性,那就去吧; 没有令人信服的理由不这样做.

(顺便说一句,Tom,我很想知道调试器发生了什么 - 我不记得在调试器中看到任何基于With语句的异常行为,所以我很想知道你看到的行为是什么.)

  • @John Rudy - 找到With语句的开头并设置断点.跳到下一行(所以你隐藏了if块下面的第一行).突出显示它,然后"添加观察".您应该看到:'With'上下文和语句在调试窗口中无效. (6认同)
  • 如果`With`与可变值类型的数组(例如`Rectangle`)一起使用,则生成的代码将与不使用`With`生成的任何内容不同. (5认同)

Joh*_*nRC 15

使用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
Run Code Online (Sandbox Code Playgroud)

所以,WITH语句不仅仅是语法糖,它真的是一个不同的结构.虽然您不太可能像上面那样编写明确的代码,但在某些情况下,这可能会无意中发生,因此您应该意识到这个问题.最可能的情况是您可能正在遍历一个结构,例如对象网络,通过设置属性可以隐式更改其互连.


phi*_*ihp 11

这都是关于可读性的.像所有的语法糖一样,它可能被过度使用.

如果 您将对象的多个成员设置为几行,请接受它

With myObject
  .Property1 = arg1
  .Property2 = arg2
...
Run Code Online (Sandbox Code Playgroud)

用"With" 避免做任何其他事情

如果你编写一个跨越50-100行的With块并涉及许多其他变量,那么它很难记住在块顶部声明的内容.出于显而易见的原因,我不会提供这种凌乱代码的示例

  • 我在这里寻找这样的答案.`with`块长于一个屏幕高度,给读者增加了不必要的负担. (5认同)

Jon*_*eet 6

在它使代码真正更具可读性的地方,去吧.当它使更少的可读性,避免它-尤其是,我建议你避免嵌套with语句.

C#3.0具有仅用于对象初始化的此功能:

var x = new Whatever { PropertyA=true, PropertyB="Inactive" };
Run Code Online (Sandbox Code Playgroud)

这不仅仅是LINQ所需要的,但它在语法不表示代码气味的位置方面也很有意义.我经常发现,当我在对象超出其初始构造时执行许多不同的操作时,这些操作应该作为单个封装在对象本身上.

关于你的例子的一个注意事项 - 你真的需要"我"吗?为什么不写:

PropertyA = True
PropertyB = "Inactive"
Run Code Online (Sandbox Code Playgroud)

?当然,在这种情况下暗示"我"......


ljo*_*era 5

我会怀疑使用了很多关键字的代码:如果它用于更容易设置大量的实例变量或属性,我认为这可能表明你的类太大(大类气味).如果你用它来代替这样的长链调用:

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"
Run Code Online (Sandbox Code Playgroud)

那么你可能违反了得墨忒耳法