Too much type information, or welcome back System.Object and boxing

We all know that generics are good - they promote code reuse, static type checking by the compiler, increase runtime performance, allow more flexible OOP designs, lay the foundation for LINQ, help the IDE to provide more helpful IntelliSense and have tons and tons of other vital advantages. "var" is another good feature, which (unlike "object"), also helps to preserve full static type information.

However I hit a rare case recently where I had too much static type information about my code, so I had to use System.Object (and boxing) to get the desired effect. I had a method that used reflection to set a property on a type, similar to this:

 static void SetProperty(object f)
{
    Type type = f.GetType();
    PropertyInfo property = type.GetProperty("Bar");
    property.SetValue(f, 1, new object[0]);
}

I also had a struct like this:

 struct Foo
{
    public int Bar { get; set; }
}

Now, I tried to set the Bar property on an instance of the struct:

 static void Main(string[] args)
{
    var f = new Foo();
    SetProperty(f);
    Foo foo = (Foo)f;
    Console.WriteLine(foo.Bar);
}

It didn't work! It printed out 0! I was puzzled. And then I realized what is happening. Since Foo is a struct, and f (thanks to var!) is also statically known to be a struct, the compiler passes a copy of the struct by value to the SetProperty method. This copy is modified, but the original f is not.

One simple change and it started working fine:

 static void Main(string[] args)
{
    object f = new Foo();
    SetProperty(f);
    Foo foo = (Foo)f;
    Console.WriteLine(foo.Bar);
}

I changed var to object, the struct was boxed into an object on the heap, the reference to this same object was passed to the SetProperty method, method set the property on the boxed instance, and (Foo) unboxed the same modified instance - the code now prints out 1 and everything is OK again.

"var" provided too much type information to the compiler - it avoided boxing, and knew that the variable is a struct, so I lost the modified value. After casting to object, we hid the extra information from the compiler and got the uniform behavior for both value types and reference types.

In my original code where I encountered this peculiar behavior (a custom deserializer that reads XML and uses reflection to set properties on objects), I was too focused on working with all types so I forgot that those can be value types as well. Since I had everything strongly typed with generics, type inference, vars and other modern goodness, the kind hardworking compiler preserved all the information for me and avoided boxing where I was expecting to get reference type behavior. Thankfully, unit-tests revealed the error 10 minutes after it was introduced (I definitely need to post about the usefulness of unit-tests and TDD in the future), so it was a quick fix to box a type into object before filling its properties.

It was an amusing experience.