The Daily Parker

Politics, Weather, Photography, and the Dog

V is for var

Blogging A to ZFor my second attempt at this post (after a BSOD), here (on time yet!) is day 22 of the Blogging A-to-Z challenge.

Today's topic: the var keyword, which has sparked more religious wars since it emerged in 2007 than almost every other language improvement in the C# universe.

Before C# 3.0, the language required you to declare every variable explicitly, like so:

using System;
using InnerDrive.Framework.Financial;

Int32 x = 123; // same as int x = 123;
Money m = 123;

Starting with C# 3.0, you could do this instead:

var i = 123;
var m = new Money(123);

As long as you give the compiler enough information to infer the variable type, it will let you stop caring about the type. (The reason line 2 works in the first example is that the Money struct can convert from other numeric types, so it infers what you want from the assignment. In the second example, you still have to declare a new Money, but the compiler can take it from there.)

Some people really can't stand not knowing what types their variables are. Others can't figure it out and make basic errors. Both groups of people need to relax and think it through.

Variables should convey meaning, not technology. I really don't care whether m is an integer, a decimal, or a Money, as long as I can use it to make the calculations I need. Where var gets people into trouble is when they forget that the compiler can't infer type from the contents of your skull, only the code you write. Which is why this is one of my favorite interview problems:

var x = 1;
var y = 3;
var z = x / y;

// What is the value of z?

The compiler infers that x and y are integers, so when it divides them it comes up with...zero. Because 1/3 is less than 1, and .NET truncates fractions when doing integer math.

In this case you need to do one of four things:

  • Explicitly declare x to be a floating-point type
  • Explicitly declare y to be a floating-point type
  • Explicitly declare the value on line 1 to be a floating-point value
  • Explicitly declare the value on line 2 to be a floating-point value
// Solution 1:

double x = 1;
int y = 3;
var z = x / y;

// z = 0.333...

// Solution 3:

var x = 1f;
var y = 3;
var z = x / y;

// z == 0.333333343

(I'll leave it as an exercise for the reader why the last line is wrong. Hint: .NET has three floating-point types, and they all do math differently.)

Declaring z to be a floating-point type won't help. Trust me on this.

The other common reason for using an explicit declaration is when you want to specify which interface to use on a class. This is less common, but still useful. For example, System.String implements both IEnumerable and IEnumerable<char>, which behave differently. Imagine an API that accepts both versions and you want to specify the older, non-generic version:

var s = "The lazy fox jumped over the quick dog.";
System.Collections.IEnumerable e = s;

SomeOldMethod(e);

Again, that's an unusual situation and not the best code snippet, but you can see why this might be a thing. The compiler won't infer that you want to use the obsolete String.IEnumerable implementation under most circumstances. This forces the issue. (So does using the as keyword.)

In future posts I may come back to this, especially if I find a good example of when to use an explicit declaration in C# 7.

Comments (4) -

  • David Harper

    4/25/2018 1:22:34 PM +00:00 |

    Your interview problem demonstrates (to my mind, at least) why inferred typing is a Bad Thing.  Languages with strong typing, like FORTRAN, C and Java, simply don't allow such ambiguity in the first place.  When I started out as a programmer, the FORTRAN textbooks all made it very clear that if you calculated 1.0/3.0 and then multiplied the result by 3.0, you would NOT get 1.0 back.  The limits of floating-point arithmetic were spelled out clearly.  Alas, that knowledge seems to have been lost, even in Computer Science courses: a few years ago, I had to explain to a colleague with a Ph.D. in Computer Science from a reputable university why, when he had inserted 0.3 into a FLOAT column in a database, he was seeing 0.2999998 when he subsequently retrieved the row in a SELECT.  The database wasn't broken; his understanding of floating-point numbers was.

  • The Daily Parker

    4/25/2018 2:40:28 PM +00:00 |

    You're not wrong, but I disagree. The interview question exposes a weakness of inferred typing only insofar as it's still necessary for developers to understand the internals of their preferred language. But in the other 99% of cases, it matters less how the compiler uses your value than what the value means in your problem space. The whole point of a Money class is that, if it passes all (600+) unit tests (including round-robin calculations and unequal penny allocations), it doesn't matter to the programmer whether it stores the value as a double, a decimal, or a string. And if you're using Money, or any class whose type matters, you're still going to have some explicit declaration someplace, as the examples show.

  • David Harper

    4/25/2018 3:19:51 PM +00:00 |

    It's unfortunate that you chose money as your example, because monetary amounts are really integer numbers of cents/pennies which are displayed *as if* they were floating-point amounts of dollars/pounds/euros purely by convention.  Database systems such as MySQL provide a separate data type called DECIMAL specifically for monetary amounts: if I define a column as DECIMAL(6,2), then insert a value such as 1234.56, MySQL will store this internally as the integer 123456, and put the decimal point back again when I retrieve the value.  If I had defined the column as floating-point, I might get 1234.559998 back, because there is no exact floating-point representation for 1234.56.

  • The Daily Parker

    4/25/2018 4:37:15 PM +00:00 |

    Float, double, and decimal all represent floating-point numbers in different ways. The System.Decimal type is a pair of integers under the hood. The first one (actually a System.Int64) is your value and the second one (actually a System.Int16) is the location of the decimal point. It's still a floating-point number; but it uses base-10 math instead of base-2 for calculations. FWIW, SQL Server 2010 and later has a Money data type that is (probably) a System.Decimal under the hood. It acts exactly like one, anyway; and since 2010 SQL Server has been purely .NET.

    Money and time zones: screwing up junior developers since 1960.

Comments are closed