C#使用泛型方法为泛型类创建接口
假设我有这段代码C#使用泛型方法为泛型类创建接口,c#,generics,C#,Generics,假设我有这段代码 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 A
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
{
}
可能吗?你是说
ModelBar bar = FooProvider.Get<ModelBar>().Create<ModelBar>();
ModelBar=FooProvider.Get().Create();
或者更确切地说
ModelBar bar = FooProvider.Get<ModelBar>().Create();
ModelBar=FooProvider.Get().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>();
}
}
公共类提供程序
{
公共静态提供程序获取{返回新提供程序();};
}
公共类提供者
{
公共T CreateDirect()
{
返回Activator.Create();
}
public TDerived CreateDerived(),其中TDerived:T
{
返回Activator.Create();
}
}
在此设置中,您可以:
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>();
类IMyInterf{}
类MyType:IMyInterf{}
类MyChildType:MyType{}
MyType tmp1=FooProvider.Get().CreateDirect();
MyChildType tmp1=FooProvider.Get().CreateDerived();
MyChildType tmp1=FooProvider.Get().CreateDerived();
这只是一个草图,没有经过编译,而是手写的,但它展示了总体思路
如果删除“CreateDirect”并将“CreateDerivated”重命名为仅创建,则会得到类似于代码的内容,但更简单。当然,如果您真的需要,您现在可以将这两个单独的类混合并压缩为一个类,但我看不出这有什么真正的意义:)
编辑:
当然,您可以引入一个
ProviderBase
和多个OrangeProvider:ProviderBase
,而不是ProviderBase
,这只是为了更容易地构建/找到合适的提供者,但是实际的Get
必须返回实际的ProviderBase
或IProvider
随着问题的发展,我将发布第二个答案。下面是一个示例类层次结构:
- 允许对许多模型类型使用相同的提供程序
- 允许以一致的方式构造许多不同的提供程序和模型
- 在编译时检查提供程序/模型兼容性
- 使用过程中不需要浇铸
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
返回
FooBase
而不是IFoo
怎么样?因为调用Get
时需要返回FooBase
。正如您所看到的,TModel
是ModelBar
,而不是ModelBase
,我无法将Foo
转换为FooBase
。我的设计思想是,我希望返回给定模型的提供程序,并且我希望在测试时能够替换此提供程序(使用模拟提供程序)。你的建议不允许这样。另外,一个供应商可以提供不同的(已知的)相关模型类型。我已经告诉过你这是一个草图/概述。最后两行显示,一个由“X”构成的提供者typef可以构造“Y”或“Z”,因此实现了多个类型的提供者typef。模拟/替换提供程序进行测试很简单:不再从Get
-返回IProvider
-现在您的静态FooProvider可以在测试环境中返回模拟。我的意思是,即使支持模拟,仍然没有必要混合使用工厂/提供者/模型类。我来自Java背景,我尝试做的事情相当简单,因为泛型的行为不同。所以,您的意思是,我需要为每种模型类型提供一个提供者?我刚刚发布了第二个示例。请看一看。您可以在代码中进行更强的编译时检查,以确定使用new()
通用约束创建新实例是否会成功:U Create(),其中U:T,new()
,这将使您能够简单地返回新的U()代码>而不是返回Activator.CreateInstance()
。是的!我从问题中复制了对象创建代码,我认为它将被更复杂的东西所取代,因为Activator可以接受Construction参数,而new()
约束不能(至少从.Net4/.5开始)。我不想让示例变得过于复杂。事实上,由于我已经编写了3个不同的示例提供程序,我已经将WhateverProvider
升级为使用new()
约束。谢谢你的建议!所以,这看起来很像我最初的实现。所有这些告诉我的是,我无法调用ProviderFactory.Get()
,这正是我试图做的。谢谢你的时间!你可以做到。ProviderFactory.Get().Create()将编译-在上面的示例中,您必须纠正的唯一一件事是让Get实现识别Foo2并为其返回正确的提供程序(这里,一个FooProvider
或WhateverProvider
,因为它们都可以构造Foo2。首先代表Foo继承,其次代表IWhatever实现)
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>();
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 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>();
}
}
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