NuGet: little known features Announcing: Your Last Options Dialog (YLOD)

Sharing Code Between Platforms

Published on Thursday, December 1, 2011 5:40:00 PM UTC in Programming

Someone recently asked me about how to use the same code base for both Windows Phone and a .NET application. This is a topic that people become more and more interested in it seems. It has the potential to save time and maintenance costs, but it can also cause you quite some headache. In this post I'm going to talk about your options regarding code sharing between multiple platforms, using my open source library PAARC as an example.

Ex Ante

Before I dive into the individual options you have, I want to emphasize that I usually do not recommend sharing view code in these scenarios, mostly for practical reasons. A Windows Phone application is or should be optimized for a user experience focusing around a low resolution touch screen, probably in portrait mode, and usually embraces the phone's design principles and specific controls (think Pivot or Panorama) that can only be found there. A desktop .NET or Silverlight application on the other hand traditionally is controlled using a mouse (which may be changed a bit with Windows 8, but that's a different story), has way more screen estate at hand, and may also use controls that are not even be available on the phone. Sharing view code like your XAML hence is hard and limiting, and it's usually not something you want if you truly adopt the look and feel of a platform.

A clear separation of views and logic, for example by making use of the MVVM pattern, is therefore one of the prerequisites that makes sharing code easier. Other situations may include scenarios where you don't even have views, for example when you're developing a non-visual component or library.

The Elegant

The most sophisticated form of reusing code between multiple platforms is to share binaries. Microsoft has introduced and made public the concept of Portable Class Libraries (PCL) a while ago, yet surprisingly few developers know about it. The whole idea behind that project is to enable you to create managed assemblies that can target multiple platforms at the same time, without any additional or even manual modifications. The supported platforms are:

  • .NET Framework 4
  • Silverlight 4
  • Windows Phone 7
  • Xbox 360

The link above not only explains Portable Class Libraries and its requirements, it also walks you through the process of creating and deploying a class library project. More importantly, it also lists the subset of assemblies generally supported by PCLs and what additional restrictions exist for individual platforms.

Limitations

Obviously the features you can use in these projects is limited by the lowest common denominator of the involved platforms. For example, you can see that the Windows.System.dll is only supported in Windows Phone and Silverlight; this means that you either cannot target the .NET Framework if you want to use features from that assembly, or that you cannot use the assembly if you also want to target .NET.

image

Apart from that you will also find that even when you can use an assembly, support for all shared APIs of the involved platforms may not have been added (yet(?)) to the PCL project. This means: the fact that e.g. a class is present in the assemblies of all platforms you want to target does not imply that it is automatically supported in and can be used from a PCL. We'll see an example of this below.

To get an idea what you can use in a Portable Class Library, your first stop should be the above link and the feature matrix displayed above. To get more detailed information, please refer to the MSDN documentation of the .NET Framework 4 Class Library. Whenever a member of a class is supported by PCL, you will find the following icon next to it:

Supported by Portable Class Library

Of course when you develop with Visual Studio, Intellisense will also help you with this, as it only displays those members that can safely be used when you're working on a Portable Class Library project.

Sample

Like I said, I want to use my PAARC project to demonstrate the various aspects of code sharing. One of the projects included in the solution actually is a Portable Class Library: "PAARC.Shared". This assembly contains all the base definitions that are used in the solution, for example: interfaces used across projects, DTOs, custom exception types, event arguments and enumerations.

At the same time, this project also is a demonstration of the limitations of PCLs. When you take a look at the "Sockets" sub-namespace you can see that it barely contains anything – just a few interfaces and event arguments. The reason for this is that sockets are one of the details that are only poorly supported by PCLs, even though the System.Net.dll assembly is on the list of included features. So, for sharing sockets related code, I had to find a different alternative.

The Necessary Evil

If you're not able to share binaries, then the next level is to at least share the code. Visual Studio supports this by a feature named linked files. The idea behind this is that one place in your solution (or even across multiple solutions) is the "master" that physically contains your source code. All other places where you use the same code only link back to that master file and do not have a physical copy of the code. The side effect is that you only have to maintain all your logic in one place, and e.g. fixes or other changes will be reflected in all other places immediately, so this successfully eliminates code duplication.

To use the feature, you first create that said master file in the project that you want to take the lead. After that, use the "Add/Existing Item…" feature of Visual Studio in your "slave" projects to let them point back to the already existing file. Instead of using the normal "Add" feature (which would create a copy of the file), use the small arrow next to that button in the add dialog to bring up a menu that allows you to choose "Add As Link":

image

This is reflected in the Solution Explorer with a small overlay on the added file's icon (similar to a Windows shortcut icon overlay):

image

As you can see, I'm making use of this feature in PAARC to link multiple files in the client part of the communication to files in the server project (the server project is my "master" in that case, because the client communication is a subset of the server communication).

Limitations

Limitations and problems of this approach are multi-faceted. First we have purely technical and cosmetic problems. For example, when you open a linked file, then Visual Studio remembers the context it was opened in (i.e. the containing project). Let's say that you open the "DefaultCommunicationFactory" file in the screenshot above by double-clicking the entry in the "ServerCommunication" project, then Visual Studio will know that the editor it opened (and the file) belongs to that project. This is a good thing per se, because this means that you e.g. also get correct Intellisense support, depending on what project you work on. However, if you now try to open the file again by double-clicking on the linked entry in the "ClientCommunication" project, you will receive an error message from Visual Studio:

image

This somewhat complicates your workflow, as you either always have to close and re-open these files, and are presented with interrupting message boxes all the time (unless you're able to precisely keep track of your open tabs, which I'm apparently not).

More severe however are the problems that arise from the different feature set of the involved platforms. Now all of a sudden you as a developer need to manually track the contexts these shared files are used in too. If you edit the above file in the context of the server side project (a .NET class library) and add something that is not supported on the phone, you'll break the setup. The same of course happens if you edit the file in the client side project (a Windows Phone class library) and add something that is not available in .NET. Luckily you'll be notified of these problems early – the next time you try to compile your solution the compiler will complain about types that are not available in the context of the current project. Fixing these issues however can be a whole different story, which brings us to the next topic.

And the Ugly

In projects that target multiple platforms other than trivial setups, you will inevitably run into the situation that some code cries for being shared, but it's not quite the same on all platforms. In PAARC, this applies to the sockets implementation as well as to the next higher level, the communication channels. The client side of the implementation is pretty much the same in both .NET and on the phone; however, the .NET side also acts as server, and all the required APIs for this are missing in Windows Phone. Now what?

Partials to the Rescue

What I usually do extensively in these situations is use partial classes. This allows you to easily separate functionality for a particular platform into a separate physical file that does not interfere with the part of the code that is shared across platforms. In the case of PAARC, I have split e.g. the communication channel implementations into one general and a server specific part each. In the client side project, I only link the files containing the general implementation, not the server part:

image

Things become a bit more complicated if not only the public API of a class is affected, but the internals of the class also need to somehow access shared implementation details. For example: the communication channel internally maintains a socket. On the server side, some events specific to server behavior (for accepting client connections) need to be hooked. However, the place where that needs to be done is part of the API that is also used on the client side (where those events are not known).

In these cases, you can construct a "hoax decoupling" using partial methods. In this particular sample, the implementation in the general part of the code looks something like this:

if (Socket == null)
{
    Socket = CommunicationFactory.CreateTcpSocket();
    Socket.Connected += Socket_Connected;
    Socket.ConnectionClosed += Socket_ConnectionClosed;
    Socket.Error += Socket_Error;

    OnCreatedSocketPartial();
}

// [...]

partial void OnCreatedSocketPartial();

This file can still safely be linked in the client project, as it does not contain any server specific API that is not available on the phone. The partial method then is ignored, as no implementation exists. On the server side however (in the ".Server.cs" partial class file), an implementation of the partial method is provided:

partial void OnCreatedSocketPartial()
{
    Socket.ClientAccepted += Socket_ClientAccepted;
}

// [...]

private void Socket_ClientAccepted(object sender, ClientAcceptedEventArgs e)
{
    // server logic
}

Some cosmetic problems you will run into when you're using this (or any of the following features below) is that for example tools like ReSharper and other code optimization add-ins will complain about these setups often. They offer to e.g. remove that partial method when you're working on the file in the context of the client side project because they do not realize it's actually required and implemented on the server side. So in that case you have to be extra careful with the automated correction and refactoring features of such tools. This may even affect the compiler which will produce warnings in certain situations because it's also not able to recognize the real nature of this setup. So if you're a fan of completely clean build logs, you may find yourself adding pragma statements to disable and re-enable warnings with that sort of code sharing.

Conditional Compilation

In a sense, conditional compilation is the next step of ugly in code sharing. Where partial classes still maintain a clear physical separation of code that belongs to different platforms, compilation symbols get yet a bit nastier and allow you to mix code for different platforms in the very same physical place.

Making a decision for either conditional compilation or partial methods often is a matter of taste or habit. I tend to use partial methods if a certain amount of logic and code is involved, and conditional compilation for shorter snippets of code. The following is an excerpt from PAARC where I have to do some additional setup for sockets on the phone platform that is not part of the .NET Framework. This is a one-liner, so there hardly is justification to complicate this with partial methods and moving it to a partial class:

public void ConnectAsync(string remoteAddress, int remotePort)
{
    var socket = new Socket(AddressFamily.InterNetwork,
        SocketType.Stream,
        ProtocolType.Tcp);

#if WINDOWS_PHONE
    // make sure we only use the socket when we have a non-cellular (Wi-Fi or similar) connection
    socket.SetNetworkRequirement(NetworkSelectionCharacteristics.NonCellular);
#endif

    // [...]
}

Other situations where I use that is when e.g. an interface forces me to implement a method even though it is not supported on the platform. Then I need a version of that method which simply throws an exception so the compiler stops complaining. While this could be perfectly solved with partial classes too, I find it more confusing to add additional files to my projects that only contain code which throws exceptions than to use conditional compilation. Again, it's often simply a matter of taste.

A special flavor of conditional compilation is the ConditionalAttribute which allows you to decorate methods (and attribute classes) that should only be invoked when a certain compilation symbol has been defined. However, since the method implementation needs to be present and compile-able, it only has limited use for cross-platform code sharing.

Limitations

The mechanisms described in this chapter are powerful and able to resolve almost all of the problematic situations with code sharing. However, I hope you can already imagine that this can become pretty ugly (hence the caption), and if you overdo this, it can turn into a maintenance nightmare quickly. If you have a lot of places where you use things like conditional compilation or partial methods, the code becomes significantly harder to read and understand for others (and probably yourself, when you return a few months later), und unnecessarily cluttered. In these cases, you may want to take a step back and ask yourself if sharing code really is the best thing to do, or maybe if there are other options that make the separation of platform specific implementation details from general code more efficient (for example derived classes). You always have to find a balance and evaluate the threshold of identical code that makes sharing truly beneficial for your process.

Conclusion

Microsoft is making progress towards a unification of multiple development platforms and a simplification of sharing code between them with its Portable Class Libraries project. However, at the moment this still is painfully limited in certain areas like sockets networking, view model sharing and others, so you have to resort to alternate approaches like linked code files. If nothing else works, conditional compilation can often provide a solution, but I usually think of it as a last resort and try to avoid it if possible, as it also comes with a lot of drawbacks.

If you have any questions, feedback or comments, feel free to contact me or post below.

Tags: .NET · Portable Class Libraries · Silverlight · Windows Phone 7