Question

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 sake of convenience and readability, I often use the With statement to set these properties. I find that

With Me.Elements
    .PropertyA = True
    .PropertyB = "Inactive"
    ' And so on for several more lines
End With

Looks much better than

Me.Elements.PropertyA = True
Me.Elements.PropertyB = "Inactive"
' And so on for several more lines

for very long statements that simply set properties.

I've noticed that there are some issues with using With while debugging; however, I was wondering if there were any compelling reasons to avoid using With in practice? I've always assumed the code generated via the compiler for the above two cases is basically the same which is why I've always chosen to write what I feel to be more readable.

Was it helpful?

Solution

If you have long variablenames and would end up 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

then I would use WITH to make it more readable:

With UserHandler.GetUser.First.User
    .FirstName="Stefan"
    .LastName="Karlsson"
    .Age="39"
    .Sex="Male"
    .Occupation="Programmer"
    .UserID="0"
end with

In the later example there are even performance benefit over the first example because in the first example Im fetching the user every time I access a user property and in the WITH-case I only fetch the user one time.

I can get the performance gain without using with, like this:

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"

But I would go for the WITH statement instead, it looks cleaner.

And I just took this as an example so dont complain over a class with many keywords, another example could be like: WITH RefundDialog.RefundDatagridView.SelectedRows(0)

OTHER TIPS

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.)

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.

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

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

I would be suspicious of code that uses a lot this keyword: if it is used to make easier to set lots of instance variables or properties I think this may indicate that your classes are too large ( Large Class smell ). If you use it to replace long chains of calls like this:

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"

then you are probably violating Demeter Law

I don't use VB.NET (I used to use plain VB) but...

Is the leading dot mandatory? If so, then I don't see a problem. In Javascript, the result of using with is that a property of an object looks just the same as a plain variable, and that is very dangerous, as you don't see if you're accessing a property or a variable, and thus, with is something to avoid.

Not only is its use easier on the eyes, but for repeated access to properties of an object, it's likely to be faster, as the object is fetched through the method chain only once, and not once for every property.

I do agree with other replies that you ought to avoid nested use of with, for the same reason as why to avoid with altogether in Javascript: because you no longer see what object your property belongs to.

The 'with' is basically the 'cascade' from Smalltalk. It is a pattern in Kent Beck's Smalltalk Best Practice Patterns book.

A summary of the pattern: use it when it makes sense to group the messages sent to the object. Don't use it if it just happens to be some messages sent to the same object.

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.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top