views:

3889

answers:

7

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.

A: 

I'm pretty sure this is VB.net only, but I could be wrong. Being a C# guy I was always a little jealous of that syntactic sugar.

kenny
C# 3.0 has it for object initialization, where it's frequently useful. If you're doing this at other times in the object's life cycle, that sounds like a code smell to me, suggesting that there should probably be one combined operation which does all the appropriate things.
Jon Skeet
Where I've seen it useful is in external objects with lots of properties, but @Jon's probably right, obj-init works there usually or throw it into a wrapper method.
kenny
There are other .Net languages that has the WITH statement.
Stefan
Delphi also has the WITH statement.
JosephStyons
A: 

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

Jon Skeet
I was just doing that for a concise example, not to really demonstrate how I'm using in real practice.
Tom
Isn't it just typical how hard it is to pull realistic examples out of thin air... they always seem simpler than a real world case and always somebody somebody comes up with a way to simplify just the example, but not the general case.
bart
+10  A: 

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)

Stefan
+1  A: 

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

ljorquera
It was only to show an example. It could be long variablenames in combination with one or two keywords..
Stefan
Like this for example: WITH RefundDialog.RefundDatagridView.SelectedRows(0)
Stefan
I'd argue that exposing the entire GridView contained in the RefundDialog to external code is also a violation of the Law of Demeter. In my experience, almost every time the "With" statement is used, there's some Law of Demeter violation occurring. Long variable names might be a case for the With statement, but I find that the developer has to be careful not to let the With block get to big or it becomes hard to track which object makes up the "With context".
Jeremy Wiebe
+1  A: 

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.

bart
The leading dot IS mandatory.
Stefan
+7  A: 

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

John Rudy
@John Rudy - Find the beginning of a With statement and set a breakpoint. Step to the next line (so you're hiding the first line right under the if block). Highlight it, then 'Add Watch'. You should see this: 'With' contexts and statements are not valid in debug windows.
Tom
Ah, interesting! I don't frequently use Watches in debugging, which is why I never ran across it. I shall have to try that -- and maybe, for grins, try it across 2003, 2005 and 2008 and see if any of them behave differently. Thanks for the tip!
John Rudy
A: 

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.

soemirno