C# 在本例中,如何在单个变量中存储从同一接口继承的具有泛型类型的不同对象?

C# 在本例中,如何在单个变量中存储从同一接口继承的具有泛型类型的不同对象?,c#,C#,假设我有一个由两个类组成的数据模型,它们都实现了相同的接口: 接口属性{} 类EntityTypeA:Entity{} 类EntityTypeB:Entity{} 我有一个通用服务,其中包含这些实体的列表,并对它们进行处理。该服务有多种不同的实现,都是从IService继承而来的,但现在我们只说一个“服务” 接口IService,其中T:class,icity{ //东西 T GetEntity(Func linq); } 类服务:iSeries,其中T:class,icity{ //东西 I

假设我有一个由两个类组成的数据模型,它们都实现了相同的接口:

接口属性{}
类EntityTypeA:Entity{}
类EntityTypeB:Entity{}
我有一个通用服务,其中包含这些实体的列表,并对它们进行处理。该服务有多种不同的实现,都是从IService继承而来的,但现在我们只说一个“服务”

接口IService,其中T:class,icity{
//东西
T GetEntity(Func linq);
}
类服务:iSeries,其中T:class,icity{
//东西
IEnumerable_实体;
}
在这一点上,我可以轻松地为各种实体创建新的服务,并与它们一起工作。向它们添加特定类型的新实体,调用方法,将它们取回,而无需手动强制转换任何内容

IService serviceA=新服务();
IService serviceB=新服务();
很好,但是现在我想把所有这些服务存储在一个地方,这样我以后就可以轻松地获取我想要的服务,而不必将它们都保存在一个单独的变量中

最终我希望能够做这样的事情:

\u manager=new ServiceManager();
_经理。添加服务(“A”,服务A);
_经理。添加服务(“B”,服务B);
iSeries服务A=_manager.GetService(“A”);
所以我试过这样的方法:

类服务管理器{
i词典服务;
public void AddService(字符串键,IService manager),其中T:class,icity{
_服务[钥匙]=(副)经理;
}
公共IService GetService(字符串键),其中T:class,icity{
返回(IService)_服务[键];
}
}
这里的问题是“无效强制转换异常”,当调用AddService(可能还有GetService)方法时,我无法将
服务
强制转换并存储到
iSeries设备
中。这对我来说有点惊讶,因为EntityTypeA实现了EntityTypeA,而服务实现了IService


因此,我的问题是:如何将所有这些通用服务存储在一个变量中,以便使用管理器的一个方法轻松获取它们?我希望此管理器是负责管理所有这些服务的单个实例,但我不知道如何在其中保存所有这些泛型类。

您不能将
服务
存储到
iSeries设备
中,因为
iSeries设备
t
上是不变的。泛型类型参数在默认情况下是不变的,除非您另外声明它们
Foo
通常不可分配给
Foo
。看看原因

根据
//stuff
IService
中的内容,您可以潜在地进行
T
,允许您将
IService
类型的值分配给
IService
类型的变量

您可以通过在
T
上添加
out
修饰符来完成此操作:

interface IService<out T> where T : class, IEntity {
    // stuff
    T GetEntity(Func<T, bool> linq);
}
因为这将破坏类型安全:

IService<IEntity> s = new Service<EntityTypeA>();
s.Foo(new EntityTypeB()); // breaks type safety! I can now give Service<EntityTypeA> a EntityTypeB object!
IService s=新服务();
s、 Foo(新实体类型b());//破坏类型安全!我现在可以为服务提供EntityTypeB对象!

为什么使用接口而不是抽象类?RegardsI认为这是一种使用接口的好方法,接口定义了将在服务中使用的方法,但每个服务都有自己的实现,这取决于它应该如何处理其中的接口。如何将其更改为摘要?你好,Rob1n,欢迎来到StackOverflow。您所描述的与IoC所做的类似(为了注册和检索服务)。请添加完整的异常,以查看是否有任何提示。我没有复制你的代码,但是这个想法对我来说还可以。你可以用与你的接口中相同的方法(抽象)声明一个抽象类。您的服务继承自抽象类(覆盖corse的方法)。现在,您可以从typ//AbstractClassName声明您的dictionary,并在其中存储您的服务。方法“GetService”返回类型//AbstractClassName。如果这似乎是一个有效的解决方案,让我知道,我会张贴一个解决方案给你!这回答了你的问题吗?我可以试试看,但是你认为这是我应该做的事情,还是我应该以不同的方式重构我的代码?每当我必须“围绕代码工作”时,我都会觉得自己做错了什么,尽管我的用例似乎有些合理。@Rob1n如果
服务
没有“采用
t
(除其他外)”的方法,你应该这样做。基本上,如果
Service
可以是协变的,那么它应该是协变的。它可以接受一个通用的Func linq查询,该查询过滤并返回其中的实体。例如:
T GetEntity(Func-linq)这意味着我不能让它协变,对吗?@Rob1n没关系。基本上,如果编译器允许,您的类可以是协变的。
IService<IEntity> s = new Service<EntityTypeA>();
s.Foo(new EntityTypeB()); // breaks type safety! I can now give Service<EntityTypeA> a EntityTypeB object!