One of the challenges of writing Windows Runtime apps with .NET is that they use a simplified version of the .NET Framework (see .NET for Windows Store apps overview ). For normal coding many of these differences aren’t a major issue: the subset still has all of the functionality needed for Windows Runtime apps, although it may require choosing appropriate methods or using Windows Runtime classes rather than .NET classes.
These differences are more difficult when trying to port code that already targets a different subset of the framework, especially if you want to maintain cross-platform compatibility.
I’ve run into this several times recently helping game developers port Unity 3d games from iOS or Android to Windows and Windows Phone. The same techniques will work to bridge the difference between any two .NET Framework library versions. Here are three methods that can be used to smooth over many of these differences:
- Switch between platform-specific behavior with conditional compilation.
- Build custom classes mimicking the behavior of unavailable classes.
- Attach custom methods to existing classes to replace unavailable methods.
Conditional compilation to choose platform-specific behavior
The most obvious is to abstract the code differences into platform specific classes, methods, or functions. Instead of directly calling file API, call a platform specific method which opens the file and returns the needed data. Depending on the app’s needs this function could return a stream or it could parse and return the data.
This method works very well if the differences are already well isolated, but if the app scatters the API use throughout the code then it could be a major rewrite.
Replacement classes to add back in missing behavior
In that case a better option may be to replace the missing functionality in place: write a class or method that mimics the original’s interface but calls the new methods.
Again: there are a few options.
The least intrusive is to create new class with the same name in a new namespace. The app can use conditional compilation directives to choose which to use.
Unity does this to mimic several common functions of the System.IO.File class in UnityEngine.Windows.File . Internally UnityEngine.Windows.File calls the asynchronous Windows Runtime classes from Windows.Storage then waits on the Task object to turn the call synchronous. Unity scripts always run on a worker thread not on the UI thread, so they can synchronize File access. Calling Task.Wait on the UI thread will raise an exception)
A game can choose between them by switching its using statements to use UnityEngine.Windows when compiling for the .NET Framework for Windows Store apps and use System.IO on other platforms:
There is an example plug-in which defines the same alternative functions in the LegacySystem namespace at https://aka.ms/unityportingsamples. It isn’t needed now that Unity provides the classes in UnityEngine.Windows, but it can be useful sample code if you need to expand for other classes and methods that UnityEngine.Windows doesn’t provide.
Since System.IO.File doesn’t exist at all in the .NET Core framework we could add our own directly to that namespace (make sure you pass valid paths):
This technique can work the other direction too: Unity’s Mono implementation is old and doesn’t include many newer .NET classes that are available in the .NET 4.5 for Windows Store apps. I was able to add my own version of System.Tuple for use in the Unity player and then use the .NET version #if NETFX_CORE.
Attached properties to add back unavailable methods
If the class already exists but we need methods that aren’t implemented then instead of replacing the whole class we can add back the missing methods as extension methods. For example, I worked with a dev whose code relied on ArrayList.IndexOf which isn’t available in the .NET Framework for Windows Store apps. We were able to add an attached property so he could tag his code onto the built in ArrayList:
These techniques greatly simplify porting code between similar-but-not-quite-the-same platforms such as Unity3d’s Mono and the .NET for Windows Store apps. You don’t need to rewrite everything to make up for a few easily-shimmable differences.
Next time you’re stuck on a missing API give these a try!
Don’t forget to follow the Windows Store Developer Solutions team on Twitter @wsdevsol. Comments are welcome, both below and on twitter.