I need to add and/or modify profiles to allow more classes and members to be shared in PCL (many of them are built-in in framework, such as Thread.Sleep). What's the best way to do this? Are there any tools to help that?
PS: I'm not seeking an anwser to tell me NO or STOP. I want to have compile-once DLLs that may be shared in different environment. No per-platform binary, no recompile, no ifdef.
Following is what I got so far:
Requirement:
PCL profiles: under Reference Assemblies\Microsoft\Framework.NETPortable:
Customization attempt #1
Customization attempt #2
Customization attempt #3
Customization attempt #4
Customization attempt #5 inherit #4
Notes:
It works to a certain degree, but quite troublesome to customize existing referenced DLLs or add new ones, nor can one easily validate PCL code after overriding referenced DLLs (if possible at all).
Since you won't take NO or STOP as an answer let me try to explain why this is a bad idea. The short answer is: if PCL doesn't expose an API it's usually because it won't work.
First of all PCL doesn't have it's own set of APIs. PCL just exposes the intersections of APIs between a given set of platforms you want to target. Now, there are cases where an API level intersection would yield more APIs than what PCL exposes. There are several reaons why this might be the case.
The first one should be obvious. PCL itself isn't an actual platform but only exposes what's there. So we can't give you APIs that don't actually exist across all the platforms you target. After all, we expose the intersection.
The second one sounds a bit strange but actually does happen. Take, for example, file IO on Windows Phone 7. Althought the File class is technically available on the Windows Phone, it's documented as
This type is present to support the .NET Compact Framework infrastructure in Silverlight for Windows Phone, and it is not intended to be used in your application code.
You may say "what do I care?" and simply try it but then you would find out that the security model on the phone would prevent you from accessing the files you are looking for. So exposing this API in PCL would not be helpful. In fact, our team believes this actually would hurt you because it leads you down a non-portable path.
The third issue around APIs being implemented in different assemblies is a bit more involved. In order to understand why this is an issue you need consider how the CLR handles APIs in general. The CLR doens't have the concept of loading indiviual types as, for example, Java does. In .NET types are implemented in assemblies and in order to use them ("load them"), you need to load the assembly that defines the type. Under the covers, type references include both, the namespace qualified type name as well as the assembly the type is defined in. In general, types that have the same namespace qualified name but live in different assemblies are considered different. For example, the type MyNamespace.MyType, Assembly1
and MyNamespace.MyType, Assembly2
. Please note that assemblies themselves have a notion of a fully qualified name as well; it includes the assembly name and the public key token. This allows two companies to produce both an assembly called "Foo" and not confuse the CLR (assuming, of course, they are signed with different keys). So in essence loading type requires a few steps: finding the assembly the type is defined in, loading that assembly, and then loading the type.
Usally, different .NET platforms use different keys for platform assemblies, for example, mscorlib. Now you may wonder how you can use types from Silverlight on the .NET Framework. The reason for that is that CLR has the concept of assembly unfification. Assembly unfication allows the .NET Framework to treat references to Silverlight's mscorlib as references to the desktop version. This is valid because Silverlight's mscorlib was designed to be a subset of the .NET Framework version (for a particular combination of versions that is).
Although this may sound like the silver bullet to bridge all paltform differences, it's actually not. Over time, different platforms chose different assembly factorings. Take, for example, ICommand. It's available in .NET 4 and Silverlight 4. However, in WPF it's implemented in PresentationCore.dll while Silverlight put it in System.Windows.dll. To understand why PCL doesn't expose ICommand when you target .NET 4 and Silverlight 4 let's see what would happen if PCL would expose it.
In PCL, we have to put ICommnad in some assembly. We could either chose to use the Silverlight assembly or the full framework one. No matter which one we would pick, the type would not resolve on the other platform as PresenationCore.dll only exists in .NET 4 and System.Windows.dll only exists in Silverlight 4.
We solved this problem by allowing references for ICommand in System.Windows.dll to succeed on the full framework. How did we do this? The answer is type forwarding. Type forwarding allows an assembly to say "I define a type Foo". When the CLR tries to load the type Foo from that assembly, the assembly actually says "no, no -- the type Foo is actually defined in this other assembly Bar". In other words, assembly Foo contains something like a pointer to Bar's version of the type. We call those pointers type forwarding entries.
This concept allowed us to solve the ICommand mismatch by adding a System.Windows.dll to the full framework that contains a type forward to the actual implementation. PCL now gives you ICommand in System.Windows.dll and can be sure type load requests can succeed on both, .NET Framework as well as Silverlight. This, however, requires to target at least .NET Framework 4.5 as the previous versions didn't had the type forward.
For all APIs that should be exposed but aren't we are working together with the platform owners to close the gaps. We have basically two strategies for that:
However, "just" addding it to PCL doesn't work.
EDIT: If you miss a feature in PCL you can always unblock yourself. Our tester Daniel has written a blog post that shows a few techniques you can use.
NO. STOP.
OK, since that's not what you want to hear, continue at your own risk. :) This certainly isn't supported, and IANAL so I don't know if it would be allowed by the license agreement either.
It sounds like the main problem you have with your existing solutions is that you can't pick individual APIs to add to the portable profile. To do that, you could use ildasm on the existing reference assemblies, then add in the APIs you want (probably by copying them from the results of running ildasm on another reference assembly), and then use ilasm to create your own versions of the reference assemblies with those additional APIs.
You'll need to delay sign and/or disable strong name key verification for the keys for the assemblies you modify this way.
Another option is to use type-forwarding, as described in my answer here. In that case you would end up with your main code being shareable as is, with a dependency on a DLL that would be different for each platform.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With