在我的代码中,我有ManagerService
类型的T
,可以是BaseManager
或AnotherManager
:
abstract class BaseManager { }
abstract class FooManager : BaseManager { }
abstract class AnotherManager : BaseManager { }
class ManagerService<T> where T : BaseManager { }
现在我想通过string
获取特定对象:
static ManagerService<T> GetService<T>(string serviceName) where T : BaseManager
{
switch(serviceName)
{
case "foo": return new ManagerService<FooManager>();
case "another": return new ManagerService<AnotherManager>();
}
throw new ArgumentException("Service not found");
}
这将是用法:
static void Main(string[] args)
{
var serviceBase = GetService("foo"); // it should return ManagerService<FooManager>
var serviceAnother = GetService("another"); // it should return ManagerService<AnotherManager>
}
不幸的是,这不起作用。我遇到错误:
Cannot implicitly convert type 'app.ManagerService<app.FooManager>' to 'app.ManagerService<T>'
。
那是怎么了?
答案 0 :(得分:3)
调用GetService<T>(string serviceName)
时,必须在编译时知道通用类型T
。要么在调用时需要指定它,要么编译器必须能够从方法的参数中派生它(但您没有此类参数)。
您可以通过消除serviceName
并改用T
来解决此问题,如下所示:
static ManagerService<T> GetService<T>() where T : BaseManager
{
return new ManagerService<T>();
}
static void Main(string[] args)
{
var s1 = GetService<FooManager>(); // returns ManagerService<FooManager>
var s2 = GetService<AnotherManager>(); // returns ManagerService<AnotherManager>
}
如果您事先不知道T
-s(运行时只知道serviceName
),则可以使用抽象基本类型BaseManager
:
abstract class ManagerService { }
class ManagerService<T> : ManagerService where T : BaseManager { }
static ManagerService GetService(string serviceName)
{
switch(serviceName)
{
case "foo":
return new ManagerService<FooManager>();
case "another":
return new ManagerService<AnotherManager>();
}
throw new ArgumentException("Service not found");
}
static void Main(string[] args)
{
var s1 = GetService("foo"); // returns ManagerService<FooManager> typed as ManagerService
var s2 = GetService("another"); // returns ManagerService<AnotherManager> typed as ManagerService
}
答案 1 :(得分:0)
通常,作为泛型的消费者,您应该在 compile 时就知道要使用的类型-它们是您知道的特定类型,并且可以直接作为通用类型参数传递,或者您自己正在自己实现实现某种通用参数,并且正在传递某些类型参数。
您正在尝试混入运行时知识。即使您可以使它工作,它也往往很脆弱,并且往往会涉及大量的强制转换,以“关闭”编译器-即使警告有效并且您要为(可能的)运行时错误交换编译时安全性
因此,如果可能的话,请尽量不要在运行时间之前“推迟”通用决策。这应该编译:
class ManagerService<T> where T : BaseManager
{
}
static ManagerService<T> GetService<T>(/* not needed? */string serviceName) where T : BaseManager
{
return new ManagerService<T>();
}
因为现在让我们让 generic 参数确定我们的返回类型。当然,目前还不清楚该字符串是否有任何用途。
即使使用原始代码,您仍然必须像在上面那样,在调用站点上提供正确类型的参数才能进行编译。
答案 2 :(得分:0)
由于您评论过要基于从数据库中获得的字符串来确定管理器的类型:
如果您在编译时不知道管理器的类型,那么也许您想使用这种dependency injection方法:
(是否可行取决于您的BaseManager和ManagerService的实现方式。)
// common interface for all manager implementations
interface IManager
{ }
// optional: basic implementation
abstract class BaseManager : IManager
{ }
class FooManager : BaseManager
{ }
class AnotherManager : BaseManager
{ }
class ManagerService
{
// constructor accepting the IManager implementation
public ManagerService(IManager manager);
}
static ManagerService GetService(string serviceName)
{
switch(serviceName)
{
case "foo":
return new ManagerService(new FooManager());
case "another":
return new ManagerService(new AnotherManager());
}
throw new ArgumentException("Service not found");
}
static void Main(string[] args)
{
var serviceBase = GetService("foo"); // Returns ManagerService using the FooManager implementation
var serviceAnother = GetService("another"); // Returns ManagerService using the AnotherManager implementation
}