0

文字列または XML からオブジェクトを逆シリアル化する目的***Converterで継承する一連のオブジェクトがあります。IConverter

public interface IConverter
{
    object Convert(object value, Type targetType);
}

(コンパイル時の型がわからないので、ここではジェネリックを使用できません)。

a を使用しConvertsAttributeて、コンバーターが変換できる型をマークし、それを Ninject として倍増させConstraintAttributeます。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class ConvertsAttribute : ConstraintAttribute
{
    public Type TargetType { get; private set; }
    public ConvertsAttribute(Type t)
    {
        TargetType = t;
    }

    public override bool Matches(IBindingMetadata metadata)
    {
        return metadata.Has("Converts") && metadata.Get<Type>("Converts") == this.TargetType;
    }
}

[Converts(typeof(Int32))]
[Converts(typeof(Single))]
[Converts(typeof(String))]
[Converts(typeof(Double))]
public class BasicConverter : IConverter
{
    public object Convert(object value, Type targetType)
    {
        return System.Convert.ChangeType(value, targetType);
    }
}

シリアライゼーション モジュールでコンバーターをバインドするときは、"Converts" の下に型メタデータを添付します。

    private void BindConverter(Type typeInfo)
    {
        var converterAttributes = typeInfo.GetCustomAttributes(typeof(ConvertsAttribute), true);

        foreach (var attribute in converterAttributes.Cast<ConvertsAttribute>())
            Bind<IConverter>().To(typeInfo).WithMetadata("Converts", attribute.TargetType);
    }

型を解決するときに、特定の型を変換するコンバーターのコンテナーを照会できます。

    private IConverter GetConverter(Type t)
    {
        return converterKernel.Get<IConverter>(metadata => t == metadata.Get<Type>("Converts"));
    }

ただし、これは、クラス コンストラクターで IKernel インスタンスを取得し、実行時にクエリを実行する必要があることを意味します。

コンストラクターで単純に要求IEnumerable<IConverter>し、各コンバーターの型で ConvertsAttribute をクエリすることもできますが、Ninject のメタデータを利用してこれを行う方法があるかどうか疑問に思います...メタデータが添付IEnumerable<IConverter>されたコンストラクターで を要求できますか? この例では、コンテナ自体にクエリを実行する必要がないことを意味するタイプのディクショナリを構築したいと考えています。IDictionary<Type, IConverter>

4

1 に答える 1

1

コンパイル時の型がわからないので、ここではジェネリックを使用できません。

コンパイル時に型がわからないからといって、ジェネリックを使用できないわけではありません。それどころか、次の理由から、ジェネリックを使用する必要があると思います。

  • ジェネリック インターフェイスを使用して、これらの型を (簡単に) バッチ登録できます。
  • 使用するコンバーターを見つけるためにすべてを要求してループする必要がなく、正しいコンバーターを一度に要求できますConvertsAttribute。汎用インターフェース)、
  • System.Convert.ChangeTypeまた、カスタム コンバーターがない任意の型に対して、デフォルト (フォールバック) 実装 ( にマップされる) を定義できます。

非ジェネリック インターフェイスは引き続き必要でありIConverter、コンパイル時の型を持たないコンシューマーはそのインターフェイスに依存できます。ただし、この非ジェネリック インターフェイスの他に、次のジェネリック インターフェイスを定義できます。

public interface IConverter<T>
{
    T Convert(object value);
}

そのインターフェイスを実装することで、カスタム コンバーターを定義できるようになりました。

public class MyTypeConverter : IConverter<MyType>
{
    public MyType Convert(object value) { ... }
}

また、「フォールバック」実装として使用できる汎用実装を定義できます。この実装は、要求された型にカスタム コンバーターが登録されていない場合に使用できます。

public class DefaultConverter<T> : IConverter<T>
{
    public T Convert(object value)
    {
        return System.Convert.ChangeType(value, targetType);
    }
}

コンシューマーはコンパイル時に利用可能な型情報を持っていないため、非ジェネリックをIConverterぶらぶらさせておくと便利です。このインターフェースの実装をコンポジション ルートの一部として定義できます。Kernelこのブートストラッパー ロジック内に配置すると、コンテナーをService Locatorとして使用せずに、 に依存することが許可されます。

public class NinjectConverter : IConverter
{
    private readonly Kernel kernel;

    public NinjectConverter(Kernel kernel)
    {
        this.kernel = kernel;
    }

    public object Convert(object value, Type targetType)
    {
        var converterType =
            typeof(IConverter<>).MakeGenericType(targetType);

        dynamic converter = this.kernel.Get(converterType);

        return converter.Convert(value);
    }
}

この実装では、リフレクションと動的型付けを使用します。これは最高のパフォーマンスのコードではありませんが、Ninject のオーバーヘッドと比較すると、オーバーヘッドはおそらく重要ではありません。

次のように、定義されたすべてのカスタム コンバーターを簡単に一括登録できるようになりました。

var assembly = typeof(IConverter<>).Assembly;

var converterRegistrations =
    from type in assembly.GetExportedTypes()
    where !type.IsAbstract && !type.IsGenericTypeDefinition
    from service in type.GetInterfaces()
    where service.IsGenericType
    where service.GetGenericTypeDefinition() == 
        typeof(IConverter<>)
    select { service, type };

foreach (var registration in converterRegistrations)
{
    kernel.Bind(registration.service).To(registration.type);
}

これを Ninject で行う簡単な方法があると思いますが (おそらくワンライナー)、正直なところ、Ninject は十分ではありません。これを読んでいる人へ: より良い方法を知っている場合は、私の回答を更新してください。

次のようにフォールバック コンバーターを登録できます。

kernel.Bind(typeof(IConverter<>))
    .To(typeof(DefaultConverter<>))
于 2012-10-21T20:10:52.157 に答える