Wednesday, August 27, 2008

There have been a number of posts about the changes introduced in .Net 3.5 SP1, but this is one I've not seen before.  I'm not sure that the title, "breaking changes" is entirely correct, but it probably got your attention :)

In .Net 3.0, there was a new class, System.Collections.ObjectModel.ObservableCollection<T> (I'm sure you can guess what it does).  It had two constructors, ObservableCollection<T>() and ObservableCollection<T>(List<T>).  Clearly the guy who wrote it was having a bad day - List<T> as a constructor parameter?  What's all that about?

This stayed the same in .Net 3.5, but changed with .Net 3.5 SP1.  Now, ObservableCollection<T> has an additional constructor, ObservableCollection<T>(IEnumerable<T>) - i.e., the one that they should have had in the first place instead of List<T>.  All is good in the world.

Now there's a nice feature in VS2008 whereby you can indicate in the project properties which version of .Net you are building for.  This means that VS makes sane choices as to which assemblies you can reference, meaning that the code you write should run just fine on a machine that's only got old versions of the framework, even if you've got all the latest spanky stuff installed.  (There's another issue to do with the compiler, but I'll cover that in another blog).

Here's the problem.  If you have .Net 3.5 SP1 installed, and you create a project that targets 3.0, VS will happily only add 3.0 references.  Including the reference to the assembly (WindowsBase, if you care) that contains ObservableCollection<T>.  Which SP1 upgraded. So you can write code that takes advantage of the new constructor, such as:

ObservableCollection<int> x = new ObservableCollection<int>(new int[] { 1, 2 });

which will compile and run just fine.

When you finally complete all your coding and have all your tests passing, you pass it to your customer confident in the knowledge that you've done a good job.  He installs and runs the code, and the very first impression he gets of your work is a MethodNotFound exception, since you're calling a constructor that he doesn't have on his machine.

So what do you do?  There's not much you can do on your development system, other than be careful, which isn't much help.  What you can, and should, do is to make sure that your build server and test environments exactly match the minimum that you expect your code to be running on.  That way, a problem like the one above will get spotted during the development process where it can be easily fixed.  Note that it's probably a good idea to turn off automatic updates on build & test machines, otherwise you'll probably end up getting stuff pushed down that you didn't want.


Just found out that FxCop has a rule to detect and warn on the usage of such methods - see Brad Abrams blog posting on the subject here.  That helps no end :)

No comments: