c#用泛型方法为泛型类创建接口
本文关键字:创建 接口 泛型类 泛型方法 | 更新日期: 2023-09-27 18:12:42
假设我有这段代码
public interface IFoo
{
}
public abstract class FooBase<TModel> : IFoo
{
public T Create<T>() where T : TModel;
}
public class Foo : FooBase<ModelBase>
{
public TModel Create<TModel>()
{
return Activator.CreateInstance(typeof(TModel));
}
}
public abstract class ModelBase
{
}
public class ModelFoo : ModelBase
{
}
public class ModelBar : ModelBase
{
}
现在,假设我有这个类
public static FooProvider
{
public IFoo Get<TModel>()
{
var provider = ...; // find which IFoo class has generic TModel
return provider;
}
}
我可以用
调用它IFoo provider = FooProvider.Get<ModelBar>(); // -> instance of Foo
但是我得到了IFoo
的一个实例,因此不能访问FooBase
的方法。是否有可能实现IFoo
(或中间抽象类)提供一个方法声明,可以调用没有(必要)转换返回值provider
?
理想情况下,我希望能够做
ModelBar bar = FooProvider.Get<ModelBar>().Create<ModelBar>();
有可能吗?
随着问题的发展,我发布了第二个答案。下面是一个示例类层次结构:
- 允许为许多模型类型使用相同的提供者
- 允许以一致的方式构建许多不同的提供者和模型
- 在编译时检查提供商/模型兼容性
- 在使用过程中不需要cast
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
}
}
公共类VeryGenericProvider: IModelProvider{public TModel Create() where TModel: 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>();
}
}
这段代码可以编译,但我还没有运行它。请注意,"test"中的最后用法确实执行类型检查,并且不需要任何强制类型转换——但是提供者的内部实现肯定需要强制类型转换一些东西——例如,请参阅手动构造对象的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
你是说
ModelBar bar = FooProvider.Get<ModelBar>().Create<ModelBar>();
或者
ModelBar bar = FooProvider.Get<ModelBar>().Create();
?
我发现前者有点奇怪,除非你有一些情况下,第一个ModelBar实际上是ModelBarBase或IModelBar,后者ModelBar实际上是最终的可构造类型。
无论如何,我也发现你的类布局有点奇怪,因为在你的代码示例中,Get()和Create()来自同一个类(Foo/FooBase)。我想你把SRP弄乱了。
我建议:
public class FooProvider
{
public static Provider<T> Get<T> { return new Provider<T>(); };
}
public class Provider<T>
{
public T CreateDirect()
{
return Activator.Create<T>();
}
public TDerived CreateDerived<TDerived>() where TDerived : T
{
return Activator.Create<TDerived>();
}
}
,在这个设置中你可以:
class IMyInterf {}
class MyType : IMyInterf {}
class MyChildType : MyType {}
MyType tmp1 = FooProvider.Get<MyType>().CreateDirect();
MyChildType tmp1 = FooProvider.Get<MyType>().CreateDerived<MyChildType>();
MyChildType tmp1 = FooProvider.Get<IMyInterf>().CreateDerived<MyChildType>();
这只是一个草图,没有编译,但它显示了总体思想。
如果你删除"CreateDirect"并将"CreateDerived"重命名为"Create",你会得到类似于你的代码,但更简单的东西。当然,如果你真的需要的话,你现在可以将这两个独立的类混合并压缩成一个,但我认为这样做没有任何意义:)
编辑:当然,为了更容易构建/找到合适的提供商,您可以引入ProviderBase<T>
和多个OrangeProvider:ProviderBase<Orange>
,而不是Provider<T>
,但实际的Get<>
仍然必须返回实际的ProviderBase<T>
或IProvider<T>
。