从switch语句返回object(T)

时间:2018-07-30 08:20:11

标签: c# generics

在我的代码中,我有ManagerService类型的T,可以是BaseManagerAnotherManager

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>'

那是怎么了?

3 个答案:

答案 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
}