Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
237 views
in Technique[技术] by (71.8m points)

c# - Handling missing types in PCL with real types existing on some of the platforms

This may be a question answered somewhere, but I can't seem to find an answer.

I am working on a project to port Mono.Data.Sqlite to Windows Phone and Windows Store, but of course this requires System.Data to be ported as well. I decided to use PCLs as this removes the need for separate assemblies for the platforms (I am also checking out Silverlight, but this is not a priority) In doing this, I have managed to get most of the functionality across except for one type - DBNull :(

This is where the problem is, WP and SL have the DBNull declared, but WinRT does not. Is it possible to do something in the single assembly to use the native DBNull on the platforms where it exists (WP and SL) and use a custom implementation on WinRT? I haven't seemed to find a way to do this. I have looked at other solutions: (a) create a PCL for SL and WP and exclude the DBNull type and another assembly for WinRT, or (b) create a single assembly that references a custom assembly for WP and SL with type forwarding to the native implementation of DBNull, and an assembly for RT with the implementation of DBNull.

Is there any other ways, or which one is better?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can solve this by using strong named assemblies and the TypeForwardedToAttribute.

First, give your PCL assembly a strong name by signing it, preferably by adding an AssemblyKeyFileAttribute to the AssemblyInfo.cs file. More details are found here.

Next, add a simple DBNull class with sufficient contents. Here is an example, modify it to your own content:

namespace System
{
  public sealed class DBNull
  {
    public static readonly DBNull Value = new DBNull();
    private DBNull() { }
  }
}

Now, you will need to create target specific assemblies with the same strong name and version as the PCL assembly. These assemblies will replace the PCL assembly in the target specific applications making use of DBNull. If this should work, it is absolutely necessary that the strong name and version are the same. If the signing file is referenced in the AssemblyInfo.cs file of the PCL project, one efficient approach is simply to add (with a link) this file to the target specific project instead of the auto-generated AssemblyInfo.cs file. Also, in the project settings, make sure that the assembly name is the same for the target specific and PCL libraries.

In the .NET Framework and Windows Phone libraries replacing the PCL library, you should now add a .cs file, for example called TypeForwarding.cs, with the following contents:

using System;
using System.Runtime.CompilerServices;

[assembly: TypeForwardedTo(typeof(DBNull))]

When this assembly is referenced (replacing the PCL assembly), the compiler will then know that it should look for the existing DBNull implementation in the target framework.

On the other hand, in the Windows Store (WinRT) library replacing the PCL, you should include the same DBNull source file that you included in the PCL library, since WinRT does not contain a pre-existing implementation of DBNull.

EDIT

First of all, a very important note! If you are using TypeForwardedToAttribute, the method or property signature in the PCL assembly must be exactly the same as the signature in the target specific consumer assembly. Otherwise the method/property will not be properly identified in the end-user application. I usually consult the MSDN documentation to obtain the correct signatures.

It also crucial that if you are using type forwarding for enum types, the numeric values of the enums must be equal to the target specific implementations. A resource that is very helpful when identifying these values is the Mono source code.

Whether you should use non-functional stubs in your PCL assembly varies from type to type, and as far as I know there is no specific support for generating consumer project assemblies from a PCL.

For example, there are some types that are available on the three targets .NET, Windows Store and Windows Phone 8, but not in PCL, like the Marshal and GCHandle classes. In this case you would provide unimplemented methods in your PCL like this:

public static class Marshal
{
  public static IntPtr AllocHGlobal(int cb) {
    throw new NotImplementedException();
  }
}

and then use type forwarding in all your consumer libraries:

[assembly: TypeForwardedTo(typeof(Marshal))]

On the other hand, there are types that are available on some but not all of the targets, and not in PCL.

In that case, if it is appropriate, you can include a working implementation in the PCL (as suggested in the original answer) and include that same implementation in the consumer project for the target that is missing this type.

If it impractical or impossible to provide a PCL implementation that works with any target, you could instead include a non-functional stub in the PCL, and provide a target specific implementation in the consumer projects.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...