The VB.NET 'With' Statement - embrace or avoid?

后端 未结 10 2457
感动是毒
感动是毒 2020-11-29 02:53

At work, I\'m frequently working on projects where numerous properties of certain objects have to be set during their construction or early during their lifetime. For the sa

相关标签:
10条回答
  • 2020-11-29 03:02

    There's a gotcha when using it with structures, aka you can't set their fields, since you're working on a local copy (made at time of entry in with block) of the "with" expression and not working with a (copy of an) object reference in that case:

    The data type of objectExpression can be any class or structure type or even a Visual Basic elementary type such as Integer. If objectExpression results in anything other than an object, you can only read the values of its members or invoke methods, and you get an error if you try to assign values to members of a structure used in a With...End With statement. This is the same error you would get if you invoked a method that returned a structure and immediately accessed and assigned a value to a member of the function’s result, such as GetAPoint().x = 1. The problem in both cases is that the structure exists only on the call stack, and there is no way a modified structure member in these situations can write to a location such that any other code in the program can observe the change.

    The objectExpression is evaluated once, upon entry into the block. You can't reassign the objectExpression from within the With block.

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

    guess the compiler could have been a bit more clever if you pass to with statement a structure name instead of an expression that returns a structure, but seems it's not

    0 讨论(0)
  • 2020-11-29 03:04

    It's all about readability. Like all syntactic sugar, it can be overused.

    Embrace it IF you're setting several members of an object over a few lines

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

    Avoid doing anything else with "With"

    If you write a With block that spans 50-100 lines and involves lots of other variables it can make it REALLY difficult to remember what was declared at the top of the block. For obvious reasons, I won't provide an example of such messy code

    0 讨论(0)
  • 2020-11-29 03:05

    Where it makes the code genuinely more readable, go for it. Where it makes it less readable, avoid it - in particular, I suggest you avoid nesting With statements.

    C# 3.0 has this feature solely for object initialization:

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

    This is not only pretty much required for LINQ, but it also makes sense in terms of where the syntax doesn't indicate a code smell. I usually find that when I'm performing many different operations on an object beyond its initial construction, those operations should be encapsulated as a single one on the object itself.

    One note about your example - do you really need the "Me" at all? Why not just write:

    PropertyA = True
    PropertyB = "Inactive"
    

    ? Surely "Me" is implied in that case...

    0 讨论(0)
  • 2020-11-29 03:06

    AVOID the WITH Block at all costs (even readability). Two reasons:

    1. the Microsoft Documentation about With...End With says that in some circumstances, it creates a copy of the data on the stack, so any changes that you make will be thrown away.
    2. If you use it for LINQ Queries, the lambda results DO NOT Chain and so each intermediate clause's result is thrown away.

    To describe this, we have a (broken) example from a Textbook that my co-worker had to ask the author about (it is indeed incorrect, the Names have been changed to protect... whatever):

    With dbcontext.Blahs
    .OrderBy(Function(currentBlah) currentBlah.LastName)
    .ThenBy(Function(currentBlah) currentBlah.FirstName)
    .Load()
    End With

    The OrderBy and ThenBy have No Effect at all. IF you reformat the code by ONLY dropping the With and End With, and adding line continuation characters at the end of the first three lines... it works (as shown 15 pages later in the same textbook).

    We don't need any more reason to search and destroy WITH Blocks. They only had meaning in an Interpreted framework.

    0 讨论(0)
  • 2020-11-29 03:10

    In practice, there are no really compelling points against it. I'm not a fan, but that's a personal preference, there's no empirical data to suggest that the With construct is bad.

    In .NET, it compiles to exactly the same code as fully-qualifying the object name, so there is no performance penalty for this sugar. I ascertained this by compiling, then disassembling, the following VB .NET 2.0 class:

    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
    

    The disassembly is as follows -- note that the calls to sb2's Append method look identical to the With statement calls for 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
    

    So if you like it, and find it more readable, go for it; there's no compelling reason not to.

    (By the way, Tom, I'm interested in knowing what happened with the debugger -- I can't recall ever seeing any unusual behavior in the debugger based on a With statement, so I'm curious to know what behavior you did see.)

    0 讨论(0)
  • 2020-11-29 03:11

    There is a difference between using With and making repeating references to an object, which is subtle but should be borne in mind, I think.

    When a WITH statement is used, it creates a new local variable referencing the object. Subsequent references using .xx are references to properties of that local reference. If during the execution of the WITH statement, the original variable reference is changed, the object referenced by the WITH does not change. Consider:

    Dim AA As AAClass = GetNextAAObject()
    With AA
        AA = GetNextAAObject()
    
        '// Setting property of original AA instance, not later instance
        .SomeProperty = SomeValue
    End With
    

    So, the WITH statement is not simply syntactical sugar, it is genuinely a different construct. Whilst you would be unlikely to code something explicit like the above, in some situations this might occur inadvertently so you should be aware of the issue. The most likely situation is where you may be traversing a structure such as a network of objects whose interconnections my be implicitly changed by setting properties.

    0 讨论(0)
提交回复
热议问题