views:

89

answers:

3

My program has heavy interaction with the operating system through Win32API functions. Now I want to migrate my program to run under Mono under Linux (No wine), and this requires different implementations to the interaction with the operating system.

I started designing a code that can have different implementation for different platforms and is extensible for new future platforms.

public interface ISomeInterface
{
    void SomePlatformSpecificOperation();
}

[PlatformSpecific(PlatformID.Unix)]
public class SomeImplementation : ISomeInterface
{
    #region ISomeInterface Members

    public void SomePlatformSpecificOperation()
    {
        Console.WriteLine("From SomeImplementation");
    }

    #endregion
}

public class PlatformSpecificAttribute : Attribute
{
    private PlatformID _platform;

    public PlatformSpecificAttribute(PlatformID platform)
    {
        _platform = platform;
    }

    public PlatformID Platform
    {
        get { return _platform; }
    }
}

public static class PlatformSpecificUtils
{
    public static IEnumerable<Type> GetImplementationTypes<T>()
    {
        foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (Type type in assembly.GetTypes())
            {
                if (typeof(T).IsAssignableFrom(type) && type != typeof(T) && IsPlatformMatch(type))
                {
                    yield return type;
                }
            }
        }
    }

    private static bool IsPlatformMatch(Type type)
    {
        return GetPlatforms(type).Any(platform => platform == Environment.OSVersion.Platform);
    }

    private static IEnumerable<PlatformID> GetPlatforms(Type type)
    {
        return type.GetCustomAttributes(typeof(PlatformSpecificAttribute), false)
            .Select(obj => ((PlatformSpecificAttribute)obj).Platform);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Type first = PlatformSpecificUtils.GetImplementationTypes<ISomeInterface>().FirstOrDefault();
    }
}

I see two problems with this design:

  1. I can't force the implementations of ISomeInterface to have a PlatformSpecificAttribute.
  2. Multiple implementations can be marked with the same PlatformID, and I dont know which to use in the Main. Using the first one is ummm ugly.

How to solve those problems? Can you suggest another design?

A: 

I think you can prepare two or more app.config files, and then inject platform dependent implementation accordingly in each.

Finally, leave the heavy task to platform dependent installation or any other deployment methods, when on Windows the Windows version of app.config is used, while on Unix the Unix version is used.

You can use an addin architecture or other complicated solutions to achieve other nice features.

Lex Li
A: 

Funny you mention Unity, have you considered using a Dependency Injection container? I believe Castle Windsor, StructureMap and ninject all may have some degree of Mono support.

Doobi
@Doobi, you are not right here. Nearly all popular DI container failed to pass MoMA tests. Unity does not pass all MoMA tests, but it runs fine on Mono in some scenarios.
Lex Li
+2  A: 

Take a look at the source for Banshee. They have a neat way of plugging in different implementations depending on the platform.

TheNextman