かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Unityで属性ベースでの構成

Enterprise Library 5.0のデフォルトのDIコンテナのUnityが便利そうではあるんだけどMEFみたいに属性でコンテナに登録ができないっぽい。ということで、こんなのでっちあげてみた。

自動登録したいクラスにこの属性をつけると登録されるみたいな感じ。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class AutoRegistAttribute : Attribute
{
    private static readonly InjectionMember[] InjectionMembersInitialValue = new InjectionMember[0];

    public AutoRegistAttribute()
    {
        this.InjectionMembers = InjectionMembersInitialValue;
    }

    public string Name { get; set; }
    public Type RegisterType { get; set; }
    public InjectionMember[] InjectionMembers { get; set; }
}

んで、この属性がついてるものをコンテナに登録する拡張。

public class AutoRegisterExtension : UnityContainerExtension
{
    private Assembly[] loadTargets;

    public AutoRegisterExtension(params Assembly[] loadTargets)
    {
        this.loadTargets = loadTargets;
    }

    protected override void Initialize()
    {
        var targetTypes = loadTargets.SelectMany(assembly => assembly.GetTypes())
            .Where(type => type.GetCustomAttributes(typeof(AutoRegistAttribute), false).Any())
            .Select(type => new
            {
                Type = type,
                Attribute = type.GetCustomAttributes(typeof(AutoRegistAttribute), false).OfType<AutoRegistAttribute>().First()
            });

        foreach (var type in targetTypes)
        {
            var registerType = type.Attribute.RegisterType ?? type.Type;
            if (string.IsNullOrEmpty(type.Attribute.Name))
            {
                this.Container.RegisterType(registerType, type.Type, type.Attribute.InjectionMembers);
            }
            else
            {
                this.Container.RegisterType(registerType, type.Type, type.Attribute.Name, type.Attribute.InjectionMembers);
            }
        }
    }
}

使ってみよう

使い方は割と簡単。登録したいクラスに属性をつけるだけ。

public interface IGreeter
{
    void Greet();
}

[AutoRegist(RegisterType = typeof(IGreeter))]
public class Greeter : IGreeter
{    
    public void  Greet()
    {
        Console.WriteLine("Hello world");
    }
}

[AutoRegist]
public class Client
{
    [Dependency]
    public IGreeter Greeter { get; set; }

    public void Execute()
    {
        this.Greeter.Greet();
    }
}

そして、Mainメソッドの中でこんな感じに使う。

var container = new UnityContainer()
    .AddExtension(new AutoRegisterExtension(Assembly.GetExecutingAssembly()));
var client = container.Resolve<Client>();
client.Execute();

これで、少しは楽が出来るかな。というか、何でこういうのが無いんだろうか?Unityのポリシー?