Where oh where is my "With...EndWith" statement? - A world apart from the everday ...

A world apart from the everday ...

Assert.IsTrue(Entries.Count == 0);

Where oh where is my "With...EndWith" statement?

ok this one might take some time to read so i apologise upfront; but i just had to get this off my chest ....
 
I do so miss the With ... End With strucutre that good old VB6 and VB.NET offers.
Why i hear you gasp in angst ... well for two reasons ...
 
1. I am LAZY !
instead of typing;
    int a = object.PropertyA;
    int b = object.PropertyB;
    ...
    int z = object.PropertyZ;
 
using “With .. End With” i could simply type the following;
  with object
    int a = .PropertyA;
    int b = .PropertyB;
    ...
    int z = .PropertyZ;
 end with
 
so by my rudementary math skill that saves me typing a lot of characters each time i want to use a property on the object.
ok not a compelling enough reason for you, how about this one then ...
 
2. With ... End With improves performance.
 
before all you C# purists out there choke and die or something, hear me out ...
 
Let's look at a classic example of accessing the rows collection in the DataTable class under a DataSet class:
 
ds.Tables("yada").rows
 
This generates the following;
 
callvirt   instance class [System.Data]System.Data.DataTableCollection
             [System.Data]System.Data.DataSet::get_Tables()
 
ldstr      "yada"
 
callvirt   instance class [System.Data]System.Data.DataTable
             [System.Data]System.Data.DataTableCollection::get_Item(string)
 
callvirt   instance class [System.Data]System.Data.DataRowCollection
             [System.Data]System.Data.DataTable::get_Rows()
 
So accessing the rows collection in a data table object results in 3 calls to 3 property getters "get methods".
What happens if we do 3 accesses to the rows collection, will there be 9 calls to "get methods" generated?
Take a look at this code example:
 
ret = ds.Tables["yada"].Rows.Count;
if(ds.Tables["yada"].Rows.IsReadOnly) {}
ds.Tables["yada"].Rows.Clear();
 
simple calls to 3 different properties on the rows collection, but is it ...
what happens in the generated IL code?
 
ldloc.0
callvirt   instance class [System.Data]System.Data.DataTableCollection
             [System.Data]System.Data.DataSet::get_Tables()
ldstr      "yada"
callvirt   instance class [System.Data]System.Data.DataTable
             [System.Data]System.Data.DataTableCollection::get_Item(string)
callvirt   instance class [System.Data]System.Data.DataRowCollection
             [System.Data]System.Data.DataTable::get_Rows()
callvirt   instance int32 [System.Data]System.Data.InternalDataCollectionBase::get_Count()
stloc.1
 
ldloc.0
callvirt   instance class [System.Data]System.Data.DataTableCollection
             [System.Data]System.Data.DataSet::get_Tables()
ldstr      "yada"
callvirt   instance class [System.Data]System.Data.DataTable
             [System.Data]System.Data.DataTableCollection::get_Item(string)
callvirt   instance class [System.Data]System.Data.DataRowCollection
             [System.Data]System.Data.DataTable::get_Rows()
callvirt   instance bool [System.Data]System.Data.InternalDataCollectionBase::get_IsReadOnly()
brfalse.s  IL_0066
 
ldloc.0
callvirt   instance class [System.Data]System.Data.DataTableCollection
             [System.Data]System.Data.DataSet::get_Tables()
ldstr      "yada"
callvirt   instance class [System.Data]System.Data.DataTable
             [System.Data]System.Data.DataTableCollection::get_Item(string)
callvirt   instance class [System.Data]System.Data.DataRowCollection
             [System.Data]System.Data.DataTable::get_Rows()
callvirt   instance void [System.Data]System.Data.DataRowCollection::Clear()
 
it actually calls the same methods 3 times.
What we want to do is to make sure that the "get methods" only get called once for all the access to the rows collection.
If we were in VB.NET this would be simple by using the with...end with statement.
 
  With ds.Tables["yada"].Rows
      Ret = .Count
      If .IsReadOnly
      End if
      .Clear
  End With
 
This creates an extra object in the methods local area, used by the IL code when executing.
 
.locals init ([0] class [System.Data]System.Data.DataSet ds,
          [1] int32 i,
          [2] class [System.Data]System.Data.DataRowCollection _Vb_t_ref_0)
 
The last local variable ([2]), is generated at compile time when the compiler finds the with statement in the VB.Net code.
The IL code now takes advantage of this extra local variable by passing the reference to the rows collection to that extra hidden variable in the locals.
 
ldloc.0
callvirt   instance class [System.Data]System.Data.DataTableCollection
             [System.Data]System.Data.DataSet::get_Tables()
ldstr      "yada"
callvirt   instance class [System.Data]System.Data.DataTable
             [System.Data]System.Data.DataTableCollection::get_Item(string)
callvirt   instance class [System.Data]System.Data.DataRowCollection
             [System.Data]System.Data.DataTable::get_Rows()
stloc.2
 
When this has been done, instead of calling all the get methods, there will now be calls directly to the hidden variable
 
ldloc.2
callvirt   instance int32 [System.Data]System.Data.InternalDataCollectionBase::get_Count()
stloc.1
ldloc.2
callvirt   instance void [System.Data]System.Data.DataRowCollection::Clear()
nop
ldloc.2
callvirt   instance bool [System.Data]System.Data.InternalDataCollectionBase::get_IsReadOnly()
brfalse.s  IL_0044
nop
 
This IL code looks much better and has fewer method calls.
FYI, if you look at the IL code the "End With" statement generates you will see it even cleans up after itself
 
ldnull
stloc.2
 
Now the kind folks at C# decided not to give use this little beauty because they though us "real" programmers had no need for this ...
Here's a proposal to try mimic the behaviour:
 
DataRowCollection drc = ds.Tables["yada"].Rows;
ret = drc.Count;
if(drc.IsReadOnly) {}
drc.Clear();
drc = null;
 
Not really as nice looking in Visual Studios as the VB.NET equivalent, but this will do the exact same thing as the VB.NET with...end with statement.
 
But this was about performance i hear you complaining, not about how nice something looks that we don't really care about ... fair point;
so let's take a look at that now;
below is a simple performance test comparing the first and the last C# code examples.
 
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
             DoWork(10000);
             DoWork(100000);
             DoWork(1000000);
             Console.ReadLine();
        }
 
        private static void DoWork(int iterations)
        {
            foo obj = new foo();
            long deltaticks = 0;
               
            // first way, without our "with...endwith" mimic.
            Console.WriteLine("Executing using the properties");
            deltaticks = obj.barslow(iterations);
            Console.WriteLine("Time to execute " + iterations.ToString() + " times:" + deltaticks.ToString() + " ticks");
   
            // second way, using our "with...endwith"
            Console.WriteLine("Exectuing through an object");
            deltaticks = obj.barfast(iterations);
            Console.WriteLine("Time to execute " + iterations.ToString() + " times:" + deltaticks.ToString() + " ticks");
        }
    }
   
    public class foo
    {
        public long barfast(int iterations)
        {
            DataSet ds = new DataSet();
   
            int ret = 0;
            long startticks = 0;
            long deltaticks = 0;
   
            ds.Tables.Add("yada");
   
            startticks = System.DateTime.Now.Ticks;
 
            DataRowCollection drc = ds.Tables["yada"].Rows;  
            for(int ix = 0;ix < iterations;ix++)
            {
                ret = drc.Count;
                if(drc.IsReadOnly) {}
                drc.Clear();
            }
   
            drc = null;
            deltaticks = System.DateTime.Now.Ticks - startticks;
            return deltaticks;
        }
 
        public long barslow(int iterations)
        {
            DataSet ds = new DataSet();
  
            int ret = 0;
            long startticks = 0;
            long deltaticks = 0;
  
            ds.Tables.Add("yada");
  
            startticks = System.DateTime.Now.Ticks;  
  
            DataRowCollection drc = ds.Tables["yada"].Rows; //just to have the same overhead as the method above, but this is not used
            for(int i = 0; i < iterations; i++)
            {
                ret = ds.Tables["yada"].Rows.Count;
                if(ds.Tables["yada"].Rows.IsReadOnly) {}
                ds.Tables["yada"].Rows.Clear();
            }
 
            deltaticks = System.DateTime.Now.Ticks - startticks;
            return deltaticks;
        }
    }
 
The result from the test is shown in table 1,
 
Iterations  | Through properties         | Through object         | Difference
10000      | 312568                          | 156284                    | 200%
100000    | 1875408                        | 781420                    | 240%
1000000  | 19535500                      | 8751904                  | 223%
 

Note that these figures are approx but it's fair to say that the difference averages around 220-260%.
 
so in summary;
i plead with Anders Hejlsberg to give us C# developers a proper "with..endwith" statement for the sake of my laziness, but more importantly for the laziness of my poor pc's processor!
Posted: Apr 07 2005, 04:50 PM by Ryan CrawCour | with 8 comment(s)
Filed under:

Comments

Brian Wilson said:

Ryan, interesting write up! You definately should be the MVP in JHB on C#. Our current MVP in JHB doesnt post stuff like this! :)

Any idea whether this is fixed in .Net V2.0?

# April 8, 2005 11:21 AM

Ryan said:

i have not heard anything about a "With...EndWith" being added to C# for v 2.0.

i do know that edit & continue has been added to C# must to the dismay of certain C# pundits. myself; i cannot wait for it simply because it will mean massive productivity gains.
# April 8, 2005 11:24 AM

Simon Stewart (C# MVP) said:

I'm always ready to take a swing at light-weight VB types like Brian, but your argument for the WITH construct is pretty good.

Whenever I've wanted to use one in C#, I've got away with using a temporary variable to prevent things like the unnecessary index lookups in a loop.

There is also the using keyword that allows for sort of similar behaviour. That obviously cleans up after itself. (Brian: that isn't the same as imports in VB)

Arrogance aside, I did speak to Anders about this. (Brian : Anders is the guy who created C#. He's the Alan Cooper of VB to put it even more simply.)
Anders didn't really see the big need for putting this keyword in. He cited (Brian: that means used as an example) the current workarounds and the potential complexity of putting it into the language. Remember also that adding a new keyword to a language is always going to annoy some people. (Think about the int with = 0;).
And, when you get nested with statements things get out of hand.

Hopefully that means that his team focussed on performance and the other cool features during their dev cycle.

Hopefully it's also less for the VS team to have to think about when they *fix* their product.

It's all down to supply and demand though.
If enough people want the with in C#, then they will probably put it in, as per edit-and-continue. Similar to BW signing the VB petition.
# April 8, 2005 11:55 AM

Ryan said:

Nice one Simon;

I agree that the currently workarounds available suffice. I personally employ this pattern when doing this kind of thing inside loops.

Just thought i'd point out the underlying issues for those that might not be aware of what is happening under the hood.

Watch this space for more "under the hood C#" ...
# April 8, 2005 12:57 PM

Ryan said:

ok in keeping with the true spirit of this blog post i'm going to be removing the personal attacks that have surfaced.

sorry guys! your comments were INCREDIBLY funny all of them, hope you understand why they're being axed.
# April 8, 2005 1:43 PM

Ernst Kuschke (C# MVP) said:

This is a ver interesting post Ryan.
I for on don't like the With...End With type syntax simply because it makes code extremely unreadable.
I remember VB6 code back in the day with nested With statements where one would continuously have to scroll up / dopwn just to see exacly With what you're working With.That said, the intellisense in VS would solve the problem, but I still just don't like it.
Remember that it isn't the With structure that really solves your problem here, but rather the way the IL gets generated by the compiler - it is certainly possible for the C# team to have it optimise code like this more efficiently without a With.
I guess it is a matter of presonal opinion. YMMV, of course ;o)
# April 10, 2005 3:39 PM

Ryan said:

I completely agree with you that the With statements sometimes make you code unreadable when they're nth levels deep.
Kinda like when If statements are nested too deep, or loops or ... you get my point.
I think if you have a passion for writing unreadable code it'll happen no matter what :)

But point taken ...
I just thought it was interesting to take a look at the IL generated by some every day tasks such as the ones shown.
Watch this space for more "C# under the hood" showing just what happens in the guts of your app.
# April 10, 2005 7:10 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: