Thursday, June 5, 2014

PCL Tips and Tricks: Extension methods to the rescue

The introduction of Portable Class Libraries, PCL, considerably facilitates the sharing of functionality across managed Microsoft and Xamarin platforms. I am very enthusiastic about the PCL concept, and I have ported several .NET Framework open source projects to Portable Class Libraries. Along the way I have stumbled across various issues of making .NET Framework source code PCL compliant. In this mini-series I'll share my experiences and workarounds to facilitate the transfer to PCL for other developers.

Now, here is a very common scenario when porting .NET class libraries to PCL:

public class SomeClassToPort
{
    public static byte[] SomeMethodToPort()
    {
        var stream = new MemoryStream();
        var writer = new StreamWriter(stream);
        writer.Write("Result: {0}, {1}", 10, "Hello");
        // some more writer operations ...

        writer.Close();   // Non-existent in PCL
        stream.Close();   // Non-existent in PCL

        return stream.ToArray();
    }
}

Correct, the Close() method is not available for streams, readers or writers in Portable Class Libraries! Since the method in practice only executes Dispose(), the Close() method has been excluded from PCL. Trying to build this code yields the following error:

error CS1061: 'System.IO.MemoryStream' does not contain a definition for 'Close' and no extension method 'Close' accepting a first argument of type 'System.IO.MemoryStream' could be found (are you missing a using directive or an assembly reference?)

Dispose() is generally available in PCL, so one solution is to replace all Close() calls with Dispose(). For those wanting to avoid the hassle and maintenance issues of replacing (potentially numerous) Close() calls, there is however a workaround.


Extension methods are a perfect fit for dealing with a scenario like the above. You can just add a static class with an arbitrary name to the PCL library, with code similar to this:

public static class CloseExtensions
{
    public static void Close(this Stream stream)
    {
        stream.Dispose();
    }

    public static void Close(this TextWriter writer)
    {
        writer.Dispose();
    }
}

Now, the compiler will "translate" stream.Close() into the call CloseExtensions.Close(stream) (analogously for the StreamWriter object) and the Portable Class Library can then build successfully. The extension methods perform practically the same operations as the Close() methods in the .NET Framework. Subsequent calls to Dispose() have no effect, so this implementation should be fail-safe. 

There is also no risk of name clashes; even when the PCL library is used in a .NET application the original Close() methods and the corresponding extension methods have completely different signatures (they can just be expressed equivalently in the code).

The Close() methods are probably the most obvious examples where this extension method technique can be applied, but whenever a PCL profile is not including an instance method of one type, the same technique may be applied. Other examples from the top of my head are:
  • several methods from the Type class which are not available for Windows 8 (f.k.a. Store or Metro apps); much of this functionality has been replaced by TypeInfo instance and extension methods,
  • Stream.BeginRead, Stream.EndRead, Stream.BeginWrite and Stream.EndWrite; again not available for Windows 8 but can be simulated using extension methods.

No comments:

Post a Comment