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

时间:2008-11-12 12:09:33

标签: 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 ?我总是假设通过编译器为上述两种情况生成的代码基本相同,这就是为什么我总是选择写出我觉得更具可读性的原因。

10 个答案:

答案 0 :(得分:64)

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

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-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"

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

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

答案 1 :(得分: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

反汇编如下 - 请注意,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语句的异常行为,所以我很想知道你看到了什么行为。)

答案 2 :(得分: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

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

答案 3 :(得分:11)

这完全取决于可读性。像所有语法糖一样,它可能过度使用

拥抱它如果你通过几行设置一个对象的几个成员

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

避免使用"

执行其他操作

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

答案 4 :(得分:6)

如果它使代码真正更具可读性,那就去吧。它使 less 可读的地方,避免它 - 特别是,我建议你避免使用语句嵌套。

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

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

这不仅是LINQ非常需要的,而且在语法不表示代码味道的地方也是有意义的。我经常发现,当我在一个对象超出其初始构造时执行许多不同的操作时,这些操作应该作为单个封装在对象本身上。

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

PropertyA = True
PropertyB = "Inactive"

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

答案 5 :(得分:5)

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

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"

然后你可能违反了Demeter Law

答案 6 :(得分:3)

我不使用VB.NET(我以前使用普通VB)但是......

前导点是强制性的吗?如果是这样,那么我没有看到问题。在Javascript中,使用with的结果是对象的属性看起来与普通变量相同,而 非常危险,因为你看不出你是不是重新访问属性或变量,因此with是值得避免的。

不仅眼睛的使用更容易,而且对于对象的属性的重复访问,它可能更快,因为对象只通过方法链获取一次,而不是每个属性一次。

我同意其他回复,你应该避免嵌套使用with,原因与为什么在Javascript中完全避免with:因为你不再看到你的属性所属的对象到。

答案 7 :(得分:3)

'with'基本上是Smalltalk的'cascade'。这是Kent Beck的Smalltalk最佳实践模式书中的一种模式。

模式摘要:在对发送给对象的消息进行分组时使用它。如果恰好是发送到同一对象的某些消息,请不要使用它。

答案 8 :(得分:2)

不惜一切代价避免 WITH Block (甚至可读性)。有两个原因:

  1. Microsoft Documentation about With...End With表示在某些情况下,它会在堆栈上创建数据副本,因此您所做的任何更改都将被丢弃。
  2. 如果将它用于LINQ查询,则lambda结果为DO NOT Chain,因此抛弃每个中间子句的结果。
  3. 为了描述这一点,我们在教科书中有一个(破碎的)示例,我的同事不得不向作者询问(这确实是不正确的,名称已被更改以保护......无论如何):

      

    使用dbcontext.Blahs
        .OrderBy(Function(currentBlah)currentBlah.LastName)
        .ThenBy(Function(currentBlah)currentBlah.FirstName)
        .Load()
      

    结束

    OrderBy和ThenBy根本没有无效。如果您通过仅删除With和End With重新格式化代码,并在前三行的末尾添加行继续符...它可以工作(如同 15页后在同一本教科书中)

    我们不再需要搜索和销毁 WITH Blocks。它们只在解释框架中具有意义。

答案 9 :(得分:1)

将结构与结构一起使用时有一个问题,也就是说你无法设置它们的字段,因为你正在使用“with”表达式的本地副本(在使用块进入时制作)并且不使用它在这种情况下的一个(副本)对象引用:

  

objectExpression的数据类型可以是任何类或结构类型   甚至是Visual Basic基本类型,如Integer。如果   objectExpression会产生除对象以外的任何东西,你可以   只读取其成员的值或调用方法,然后得到一个   如果您尝试将值分配给a中使用的结构的成员,则会出现错误   随着...结束与声明。这是你得到的同样的错误   调用返回结构并立即访问的方法   并为函数结果的成员赋值,例如   GetAPoint()。x = 1.两种情况下的问题都是结构   仅存在于调用堆栈上,并且无法修改   在这些情况下,结构成员可以写入这样的位置   程序中的任何其他代码都可以观察到更改。

     

在进入块时,objectExpression被计算一次。您   无法从With块中重新分配objectExpression。

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

如果你传递给一个结构名而不是一个返回结构的表达式,那么编译器可能会更聪明一些,但似乎不是