How to make sharing code between .NET and Silverlight (a little) less painful

Windsor ships for both .NET and Silverlight. However working with the codebase in such a way that we can target both runtimes at the same  time with the least amount of friction possible proved to be quite a challenge.

Problem

We’re using single .sln and .csproj files to target both runtimes (actually 5 of them, as we ship for 3 versions of .NET and 2 versions of Silverlight, but I digress) with some heavy (and I mean-  really heavy) Jedi trickery in MsBuild by Roelof.

We’re using conditional compilation to cater for incompatibilities between frameworks. For example, SerializableAttribute and NonSerializedAttribute are not present in Silverlight, so many of our classes look like this:

#if !SILVERLIGHT
   [Serializable]
#endif
   public class ParameterModel
   {
      // some code here
   }

This is quite cluttering the code making it harder to read. It adds friction to the process, since I have to remember each time to exclude the attribute from Silverlight build. Even more annoying is the fact, that ReSharper is not very fond of pre-processor directives in code, and certain features, like code clean up and reordering don’t really work the way they should.

Solution

I figured out a way to cut the need to use conditional compilation in this case. So that I can write the code like this:

[Serializable]
public class ParameterModel
{
   // some code here
}

and still have it compile properly under Silverlight, outputting exactly the same code as the first sample. How you ask? – using a not very well known feature of .NET BCL – ConditionalAttribute.

Entire trick works like this: I replicate the Attributes conditionally for Silverlight builds, applying ConditionalAttribute on top of them with some fake condition that never will be true. That way the compiler will not complain when I compile the code, because the attributes types will be defined and visible, however thanks to ConditionalAttribute they won’t be applied to any of my types. To make them not visible to the outside world, I make them internal. Here’s what it looks like:

#if SILVERLIGHT
namespace System
{
    using System.Diagnostics;

    [Conditional("THIS_IS_NEVER_TRUE")]
    internal class SerializableAttribute : Attribute
    {
    }

    [Conditional("THIS_IS_NEVER_TRUE")]
    internal class NonSerializedAttribute : Attribute
    {
    }
}
#endif

I am on a horse.

Comments

Clever.

(OT: it’s funny to see "posted @ Tuesday, November 16, 2010 8:18 PM" when it’s 12:21 PM here:), it seems i commented on a post that is yet to be written)

Hehe,

well it is after 8PM here 😉