Yeah, you read that title right. This post is going to show you how to write platform specific code using PCLs. Now, you may be thinking that I will show you some trick with an abstract base class or possibly an interface that’s gets created in the PCL, and then the platform implements the necessary code. Well, I hate to burst your bubble, but I’m not using abstract classes or interfaces. Ahhh.. okay Brian, I know what you mean. You’re talking about shared projects. Nope! Wrong again.
In this post, we will implement platform specific functionality using a PCL, and then file linking to a file that exists in the same PCL which uses pre-compiler directives. Wait… what? Let me just show you.
So I had an interesting requirement today. I have a ton of core code in a PCL that various different platforms have to reference. One of the classes had a single method that required platform specific functionality. Now, it would really suck to have to pull this one class out of the PCL and then duplicate 99% of the code on the other platforms just to have one method implementation be different. I had to keep the class name the same across all platforms and located in the same assembly. Well, this make things very difficult. So I started playing around and made an awesome discovery.
You can have a project reference a PCL, and also have that same project link to an existing file in that same PCL that has pre-compiler directives.
Let’s take a look at a very simple example. I have created a PCL called PCLFileLinking which contains a single class called Executer.
{
private string _message = "Message from PCL";
public void Execute()
{
Debug.WriteLine(_message);
}
}
As you can see this class has one method on it called Execute, which simply writes a message to the output window. So let’s go ahead and create a few applications that target different platforms and add a reference to this PCL.
I have added a Console application, Silverlight applications, and a WPF application. Each of these projects have a reference to the PCLFileLinking project.
Let’s take a look at how each platform invokes this code.
Console:
{
Executer executer = new Executer();
executer.Execute();
System.Console.WriteLine("check output windows for message");
System.Console.ReadKey();
}
Silverlight:
{
Executer executer = new Executer();
executer.Execute();
}
WPF:
{
Executer executer = new Executer();
executer.Execute();
}
So no matter which application we run, the message in the output window will be the same.
Well, now let’s say that I wanted the WPF application and the Silverlight application to output a different message. Since we are dealing with a PCL, we are kind of stuck. I can’t throw platform specific code into a PCL. The other way I know to get around this issue is to used linked files with pre-compiler directives, but that would mean that I wouldn’t use a PCL, but rather a different assembly or just linking files directly from the different platform applications. This does not always align the the architecture or structure of my projects and APIs.
Wait…! I know I can add pre-compiler directives to a class in a PCL. So, why can’t I just link to a file that has pre-compiler directives that is defined in the PCL I just referenced? Is that possible? Will I get a compiler error? Well, let’s find out. Let’s modify our Executer class that is defined in our PCL and add some pre-compiler directives.
{
#if WPF
private string _message = "Message from WPF";
#elif SILVERLIGHT
private string _message = "Message from Silverlight";
#else
private string _message = "Message from PCL";
#endif
public void Execute()
{
Debug.WriteLine(_message);
}
}
Of course we have to make sure we edit the “Build” properties of the other platform projects to define these directives we are expecting. Now let’s link to this file as an existing item to each platform we need a different message for.
Now our solution looks like this:
Notice how we only linked to the file in the WPF and Silverlight applications. Now let’s run each platform and check what message is sent to the output windw.
Console:
Silverlight:
WPF:
Now everything works as I would expect. The Console application takes the default message, but the Silverlight and WPF applications get the platform specific implementation of the message.
BAM! That’s what I’m talking about!
I had no idea this was possible, and never even thought to try it until I got desperate. I honestly don’t know why or how this works, so if you happen to know, please share.
Check out sample app that demonstrates how it works.
As always, feel free contact me on my blog, connect with me on Twitter (@brianlagunas), or leave a comment below for any questions or comments you may have.
Awesome!
It seems that when both a PCL version and a non PCL versions of same class (complete name including namespaces) exist, .Net runtime choose to take non PCL one.
Do you think it can be a stable feature?
Yes, it appears to be a stable feature. I have run numerous tests with no issues.
At this point are you really using the PCL? or is the linked file simply being compiled into the platform specific application (WPF and Silverlight)?
Both! I am using the PCL, since I have access to all the other classes defined within the PCL assembly. The only difference is that the linked file is overriding the class definition for that one class in the PCL. I’m honestly not quite sure what the compiler is doing under the covers to make it work, but it’s a very useful ability.
Hi Brian, I guess your finding has nothing to do with any compiler tricks whatsoever.
Linking Executer.cs into your program is equivalent to copy-pasting the same code directly in that project, as far as I understand what is happening under the covers. The compiler has no idea that the file belongs to the PCL and that it should somehow magically replace your PCL class with the special variant of it.
Try calling Executer.Execute directly from within the PCL, and you’ll see that it is indeed the PCL version of it that will be called.
You are just shadowing the class Executer defined in the PCL with one defined in your program assembly. And when calling into Executer from the program, you will just be calling into the local one (the onw that was defined in your program assembly), not into the one defined in the PCL.
Sorry to disappoint you. Or did I miss something essential here?
Thanks for the explanation. I wasn’t sure exactly why it was working. I was expecting a compilation error since I had two classes, that marked as partial, with the same name and in same namespace (one linked, the other referenced). I guess that was my biggest question.