しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET vNext で使われている Assembly Neutral Interfaces について

ASP.NET vNext では新しく Assembly Neutral Interfaces という、Roslyn を利用したアセンブリから中立なインターフェースが多く使われるようになりました。

アセンブリ中立なインターフェースって何だ?という感じですが、これが結構面白い仕組みで実現されているので試しました。まずは ASP.NET チームの David Fowler 氏による解説を読んでおきます。

Assembly Neutral Interfaces
Assembly Neutral Types Implementation

後は ASP.NET vNext の Wiki にも同じような説明ですが載ってます。

Home · aspnet/Home Wiki · GitHub

Logger を使った解説が分かりやすいです。要約するとインターフェースだけを集めた Contract なアセンブリを用意する必要なく、それぞれのアセンブリから独立したインターフェースや enum を定義出来る機能です。

TypeScript の Structural Typing のような、違うような。同じ名前空間かつ同じ名前のインターフェースを用意して継承すれば、アセンブリの参照無しでダックタイピング的に使えるようになります。

1.0.0-alpha4 で試す

Visual Studio "14" CTP 4 で実際に使ってみました。まずは vNext ConsoleApp を 1 つと vNext ClassLibrary を 2 つ用意して、それぞれを Console App で参照するように設定しました。

f:id:shiba-yan:20141104210541p:plain

そしてコードは以下のような感じで用意しました。

Class1.cs

namespace ClassLibrary1
{
    public class Class1 : ConsoleApp1.IGreeting
    {
        public string Hello()
        {
            return "Hello, Class1";
        }
    }
}

namespace ConsoleApp1
{
    [AssemblyNeutral]
    public interface IGreeting
    {
        string Hello();
    }

    [AssemblyNeutral]
    public class AssemblyNeutralAttribute : Attribute { }
}

Class2.cs

namespace ClassLibrary2
{
    public class Class2 : ConsoleApp1.IGreeting
    {
        public string Hello()
        {
            return "Hello, Class2";
        }
    }
}

namespace ConsoleApp1
{
    [AssemblyNeutral]
    public interface IGreeting
    {
        string Hello();
    }

    [AssemblyNeutral]
    public class AssemblyNeutralAttribute : Attribute { }
}

Program.cs

namespace ConsoleApp1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Greeting(new ClassLibrary1.Class1());
            Greeting(new ClassLibrary2.Class2());

            Console.ReadLine();
        }

        private static void Greeting(IGreeting obj)
        {
            Console.WriteLine(obj.Hello());
        }
    }

    [AssemblyNeutral]
    public interface IGreeting
    {
        string Hello();
    }

    [AssemblyNeutral]
    public class AssemblyNeutralAttribute : Attribute { }
}

ASP.NET vNext では AssemblyNeutralAttribute という名前の属性が付いている場合、そのクラスやインターフェースはアセンブリから中立なものとして扱われます。

パッと見た感じでは IGreeting インターフェースと AssemblyNeutralAttribute クラスをそれぞれで定義しているのでコンパイルエラーになりそうですが、実際にコンパイルすると問題なく完了します。*1

f:id:shiba-yan:20141104211248p:plain

エディタでは Greeting メソッドに Class1 と Class2 のインスタンスを渡せないエラーが出てますが、実行してみると以下の通り。意図したとおりに実行されました。

f:id:shiba-yan:20141104212500p:plain

このあたりの実装は ASP.NET vNext の Roslyn でカスタマイズされています。基本的に実行時にメタデータをこねこねして正しいインターフェースでの解決が行われるので、かなり祖結合が実現できている感じです。*2

ASP.NET vNext は Assembly Neutral Interfaces 以外にも、様々な新しいテクニックが使われているので、思ったよりもコードを追うのが大変です。

*1:しかしエディタでは警告とエラーの雨嵐になる

*2:多分インターフェースの変更にも強い