Handling Windows Phone 8 and Windows 8 platform differences

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

There will be situations where at first it seems that the code you have isn’t a good match for any of the sharing techniques described in this section because you need to implement features or functionality that are specific to one platform. But with some refactoring, you can share common code and then specialize using one of the following, general, coding techniques.

This topic contains the following sections.

Conditional compilation

Sometimes you’ll have app logic for your Windows Phone 8 and Windows 8 apps that needs to be implemented in a slightly different way for each platform. In this case, you can define a directive so that one code path is compiled for Windows Phone 8 and the other for Windows 8. This technique doesn’t scale very well and can create code that’s harder to maintain, but it is very useful when you don’t want to implement any of the other sharing patterns. For more info, see Conditional compilation with preprocessor directives.

Partial classes (C#, VB only)

In the .NET Framework you can split your class, interface, or structure into partial classes, which are multiple files that each implements part of the class.

namespace ProjectB
{
    public class MyClass
    {
        public void CommonMethodA()
        {
            // code that is common to Windows Phone 8 and Windows 8
        }
 
        public int CommonMethodB()
        {
            int result = 0;
 
            // code that is common to Windows Phone 8 and Windows 8
 
            return result;
        }
 
        public void PlatformSpecificMethod()
        {
            // code that must be written for each platform
        }
    }
}

In this code example, we have a method that we need to implement specifically for Windows Phone 8 and Windows 8. This might be necessary if the code relies on API that is platform-specific. We can refactor the code so that one partial class implements the common code and another implements the platform-specific code. The following code example shows how this is done. Notice the use of the partial keyword below. This partial class defines the methods that can be shared across both apps. We can save this class to a common shared folder outside of the project folders and then add to each project using Add as Link. The file is written once, but shared across both apps.

 
namespace App.Linked
{
    public partial class MyClass
    {
        public void CommonMethodA()
        {
            // code that is common to Windows Phone 8 and Windows 8
        }
 
        public int CommonMethodB()
        {
            int result = 0;
 
 
            // code that is common to Windows Phone 8 and Windows 8
 
            return result;
        }
 
    }
}

We then create a new class in each project, and then implement the platform-specific pieces in that class.

namespace ProjectB
{
    public partial class MyClass
    {
        public void PlatformSpecificMethod()
        {
            // code that must be written for each platform
        }
    }
}

This is a straightforward way to extract common functionality from managed code and implement platform-specifics that target each platform. If the amount of common code is large relative to the amount of platform-specific code, this method of code sharing can be very useful. One thing to notice about this method is that you cannot specify what each platform-specific partial class should implement. This ability to diverge can be an advantage or a disadvantage. The compiler will catch cases when you are missing a method implementation, but there’s no contract at design time. For more info about using partial classes, see Partial Classes and Methods (C# Programming Guide) and Partial (Visual Basic).

Inheritance using base classes

When you have common code in two classes and both seem to belong to a common type, you can create a parent, or base, class and move the common code to that class. This way you can eliminate duplicate code. Another usage of base classes is extensibility, when you don’t have control on the source code of a class but would still like to add or change some behavior. You can use inheritance and override certain methods. In the case of our app, we have code that is common, but there’s a need to implement certain parts of the class for each platform. You can do this with the same class as the previous section.

namespace ProjectB
{
    public class MyClass
    {
        public void CommonMethodA()
        {
            // code that is common to Windows Phone 8 and Windows 8
        }
 
        public int CommonMethodB()
        {
            int result = 0;
 
            // code that is common to Windows Phone 8 and Windows 8
 
            return result;
        }
 
        public void PlatformSpecificMethod()
        {
            // code that must be written for each platform
        }
    }
}

This time we’ll refactor to define a base class.

namespace ProjectB
{
    public abstract class MyBaseClass
    {
        public void CommonMethodA()
        {
            // code that is common to Windows Phone 8 and Windows 8
        }
 
        public int CommonMethodB()
        {
            int result = 0;
 
            // code that is common to Windows Phone 8 and Windows 8
 
            return result;
        }
 
   public abstract void PlatformSpecificMethod();
    }
}

This is an abstract class, which means it can’t be instantiated. Instead, a class must be defined that derives from this base class. Notice also that the method we need to implement on both platforms is now defined in the base class as an abstract method. It must be implemented in the derived class. The base class can be shared with each app project as a linked file using Add as Link. It’s written once and then shared across the projects. Another alternative is to add this class to a Portable Class Library and share this one binary between the apps. For more info about sharing using Portable Class Libraries, see Share functionality using Portable Class Libraries. After we’ve defined the base class, we can then derive a platform-specific class from it and implement the platform-specific features in that class.

public class MyWin8Class : MyBaseClass
{
    public override void PlatformSpecificMethod()
    {
        // Implement this method specific to Windows 8
    }
}
 
public class MyWP8Class : MyBaseClass
{
    public override void PlatformSpecificMethod()
    {
        // Implement this method specific to Windows Phone 8
    }
}

You can see we’ve implemented a class in each project that derives from the base class and implements the platform-specific method. For more info about using abstract classes, see Abstract and Sealed Classes and Class Members (C# Programming Guide) and MustInherit (Visual Basic).

Polymorphism using interfaces

An interface is useful when you need runtime polymorphism. The purpose of an interface is to define functionality so that you can have multiple implementations of it. In code that consumes the functionality, you can just reference the interface, instead of a specific implementation. You call the methods of the interface without needing to know what the implementation is. Using code that’s similar to the code already used in the preceding example, we modify the class to create a constructor that takes an interface as a parameter. We assign this parameter to class member called _platformImpl. Then, in our PlatformSpecificMethod call, we simply call the method on the interface.

namespace ProjectB
    {
        public interface IPlatformSpecificCode
        {
            void PlatformSpecificMethodImpl();
        }

        public class MyClass
        {
            private IPlatformSpecificCode _platformImpl;

            public MyClass(IPlatformSpecificCode platformImpl)
            {
                _platformImpl = platformImpl;
            }

            public void CommonMethodA()
            {
                // code that is common to Windows Phone 8 and Windows 8
            }

            public int CommonMethodB()
            {
                int result = 0;

                // code that is common to Windows Phone 8 and Windows 8

                return result;
            }

            public void PlatformSpecificMethod()
            {
                _platformImpl.PlatformSpecificMethodImpl();
            }
        }

        // Windows 8 app project
        public class MyWin8Implementation : IPlatformSpecificCode
        {

            public void PlatformSpecificMethod()
            {
                // Implemented for Windows 8
            }
        }

        // Windows Phone 8 app project
        public class MyWP8Implementation : IPlatformSpecificCode
        {

            public void PlatformSpecificMethod()
            {
                // Implemented for Windows Phone 8
            }
        }

In this code we implement the interface in each app project, and somewhere in that project we construct MyClass and pass in the implementation. This is shown in the following snippet.

            // Construct MyClass, passing in the Windows 8 implementation of the interface
            MyClass myClass = new MyClass(new MyWin8Implementation());

           // Construct MyClass, passing in the Windows Phone 8 implementation of the interface
            MyClass myClass = new MyClass(new MyWP8Implementation());

Injecting platform-dependent code using interfaces is a very powerful pattern, and more sophisticated patterns have been created based on this simple premise. Service Locator, Factory and Dependency Injection, Inversion of Control and Dependency Injection using IoC Containers are all well-known patterns based on this concept. Many third-party toolkits and frameworks are available that offer implementations of these patterns. Used in conjunction with MVVM they form a very powerful way to share implementation and inject specializations when needed.

Another advantage of using interfaces is easier unit testing. Unit tests written to exercise the code in a class can mock an interface implementation to inject predictable behavior into the tests or to simplify the test by implementing the interface such that no services, data connections, or other dependencies are required for the unit tests.

See Also

Other Resources

Polymorphism (C# Programming Guide)

Build 2012: How to Leverage your Code across Windows Phone 8 and Windows 8

Code Sample: PixPresenter