質問が進化するにつれて、2番目の回答を投稿しています。以下は、サンプルのクラス階層です。
- 多くのモデル タイプに同じプロバイダーを使用できます
- 一貫した方法で多くの異なるプロバイダーとモデルを構築できます
- コンパイル時にプロバイダー/モデルの互換性をチェックします
- 使用中にキャストする必要はありません
コード:
public interface IWhatever { }
public class Foo { }
public class Foo2 : Foo, IWhatever { }
public class Bar { }
public class Bar2 : Bar { }
public class Bar3 : Bar, IWhatever { }
public interface IModelProvider<T>
{
U Create<U>() where U : T;
}
public class FooProvider : IModelProvider<Foo>
{
public U Create<U>() where U : Foo
{
// create a proper "U" - for example Foo or Foo2
return Activator.CreateInstance<U>(); // simpliest
}
}
public class BarProvider : IModelProvider<Bar>
{
public U Create<U>() where U : Bar
{
// create a proper "U" - for example Bar, Bar2 or Bar3
// more verbose
if (typeof(U) == typeof(Bar)) return (U)new Bar();
if (typeof(U) == typeof(Bar2)) return (U)(object)new Bar2();
if (typeof(U) == typeof(Bar3)) return (U)(object)new Bar3();
throw new Exception();
}
}
public class WhateverProvider : IModelProvider<IWhatever>
{
public U Create<U>() where U : IWhatever, new()
{
// create a proper "U" - for example Foo2 or Bar3
return new U(); // really the simpliest
}
}
public class VeryGenericProvider : IModelProvider { public TModel Create() where TModel : new() { return new TModel(); } }
public class ProviderFactory
{
public static IModelProvider<T> Get<T>() where T : new()
{
// somehow choose a provider for T, dumb implementation just for example purposes
if (typeof(T) == typeof(Foo)) return (IModelProvider<T>)new FooProvider();
if (typeof(T) == typeof(Bar)) return (IModelProvider<T>)new BarProvider();
if (typeof(T) == typeof(IWhatever)) return (IModelProvider<T>)new WhateverProvider();
return VeryGenericProvider<T>();
}
}
public static class ProviderTest
{
public static void test()
{
Foo foo = ProviderFactory.Get<Foo>().Create<Foo>();
Foo2 foo2 = ProviderFactory.Get<Foo>().Create<Foo2>();
// Bar2 bar2 = ProviderFactory.Get<Foo>().Create<Bar2>(); - compile error
Bar2 bar2 = ProviderFactory.Get<Bar>().Create<Bar2>(); // - ok!
Bar3 bar3 = ProviderFactory.Get<IWhatever>().Create<Bar3>();
}
}
このコードはコンパイルされますが、実行していません。「テスト」での最終的な使用法では型チェックが実行され、キャストは不要であることに注意してください。ただし、プロバイダーの内部実装では、いくつかのことをキャストする必要があります。たとえば、オブジェクトを手動で構築する BarProvider を参照してください。U が Bar2 であることが確実に「わかっている」場合でも、U の制約を Bar から Bar2 にアップグレードするように言語に指示する方法はありません。したがって、二重キャストが必要です。
私はこれを意味します:
return (U)new Bar(); // is ok because U is constrained to 'Bar'
対
return (U)new Bar2(); // will not compile, U:Bar is not related to Bar2:Bar
return (U)(object)new Bar2(); // is ok: temporary cast to object 'erases' type information