VB.NET – arrghh!

I get really annoyed when smarmy C# developers look down their noses at VB developers.

If I’m feeling like an argument I’ll ask them to write something in C# that I couldn’t replicate in VB. I’m not saying it’s impossible, but so far nobody has been able to do it (feel free to post a comment below if you’d like to try).

All of the Trade Me code is VB, and on the whole it does a perfectly good job. (btw, there is a good reason why we decided to migrate to VB rather than C#)

But, really, sometimes VB just doesn’t help itself.

Witness this recent email from Annie, one of the developers at Trade Me …


From: Annie
To: Development
Subject: Rounding numbers in VB.NET and SQL Server

Hey guys,I’ve just been debugging a situation in the sell process (for way too long) where two decimal numbers (9.5 and 10.5) were BOTH rounding to 10 by a call to exactly the same code: a simple call to good ol’ CLng.After searching around, it turns out that CLng, CInt, CByte, CCur and Round all implement what is known as “Banker’s Rounding” in VB.NET. This is also sometimes called “round to nearest”, or “round to even”. Basically, Banker’s rounding rounds 0.5 up sometimes and down other times. Apparently the convention is to round to the nearest EVEN number. Therefore, 1.5 and 2.5 would both round to 2. Likewise, 3.5 and 4.5 would both round to 4.If you’re interested, the rational behind this is that if you were rounding large sets of numbers, constantly rounding 0.5 UP would result in a bias as only 4 of 9 numbers (0.1, 0.2, 0.3 and 0.4) would cause a round DOWN and the remaining 5 numbers (0.5, 0.6, 0.7, 0.8 and 0.9) would cause a round UP.

ANYWAY, as you can imagine, this causes what seems to be slightly erratic un-deterministic behaviour to those of us who are used to “arithmetic rounding”. And unfortunately, it looks like there are quite a few calls to CLng particularly in the sell process. Calls to CLng are currently being used when checking for minimum or maximum values for attributes and eventually for storing and displaying integer values entered for said attributes. For example, if I enter 49.5m2 for the floor area of the apartment I’m trying to sell, it will round it up to 50m2. But if I enter 50.5m2, it will also round to 50m2. I dunno about you guys but I was always taught to round 0.5 UP in school, without exception. Anyhoo, some users might consider this to be a bug (I know I find it weird).

To add to the confusion, I just did a bit of a test in SQL Server and it looks like casting a decimal value to an int just truncates the number to the most precise digit (e.g. cast(10.9 as int) = 10).

So, basically just use a bit of caution when using CLng, CInt, CByte, CCur and Round in VB.NET and cast in SQL Server as they may be returning results quite different to what you’d expect, and worse yet, different results when calculated in the code as opposed to a stored proc.

Apparently Format and FormatNumber perform “arithmetic rounding” and although they spit out a string, in many of our cases these functions will do the trick as we tend to treat most of our integers in the sell process as strings anyway.

But yeah, just thought I’d share this with all of you. Please feel free to add to this discussion/rant.

/Annie

5 thoughts on “VB.NET – arrghh!”

  1. i will be that smarmy bugger!

    i played with anonymous delegates at home the other day (see below). it’s just syntactical sugar but it is something you cannot do in vb. another more basic example would be literal strings.

    these can be done in vb, just not as simply (imo etc etc)…

    public IList GetUnansweredMessages()
    {

    return GetItems(“spc_incoming_message_get”
    , delegate(SqlDataReader dr)
    {
    Model.IncomingMessage message = new Model.IncomingMessage();

    message.Body = dr.GetString(“body”);

    return message;

    }
    , new SqlParameter(“@message_filter”, 0));

    }

    delegate T PopulateDataReader(SqlDataReader reader);
    IList GetItems(string procName, PopulateDataReader method, params object[] paramArray)
    {
    List list = new List();

    using (SqlDataReader dr = ExecuteReader(procName, paramArray))
    {
    while (dr.Read())
    {
    list.Add(method(dr));
    }
    }

    return list;
    }

  2. Try cost calculations based on source and destination timezone! Or better yet, trying to avoid introducing floating point errors in money calculations!

    I’ve come to the conclusion that money sucks, and the only solution is to stick my head in the sand and cry.

  3. Hrm, anonymous methods and generators/iterators are pretty powerful declarative/functional language features which are sorely lacking in VB (and Java).

    C# anonymous methods also support lexical closures (unlike Java anonymous inner classes).

    You could replicate anonymous methods and lexical closures with a lot of code in VB.NET (temporary classes, etc) but replicating complicated iterators would not require just code, but a lot of algorithm/state-machine design and thought for every individual iterator you write.

    You can find examples of both features all over the web so I won’t bother posting examples here…

    I’m a pretty big advocate of functional and declarative languages. I use both anonymous methods and iterators *extensively* in most of my C# projects.

Comments are closed.