我误解了继承中显而易见的事情。我已经创建了一个示例控制台应用程序,它具有一些可以相互继承的接口和类。
使用工厂,我正在尝试根据给定的密钥检索适当的服务。但编译器说它无法将具体类添加到我要求的通用字典中。
这是我正在尝试做的事情:
鉴于工厂,请通过keyname获取服务。然后在此服务上执行Search
方法。
class Program
{
static void Main(string[] args)
{
var animalFactory = new AnimalFactory();
// Search for some fishes.
var fishService = animalFactory.GetAnimalService("fish");
var fishes = fishService.SearchForAnimals(new FishSearchOptions {ScalesType = "shiny green"});
// Search for meows.
var catService = animalFactory.GetAnimalService("cat");
var kittyCats = catService.SearchForAnimals(new MamalSearchOptions {HairColour = "golden"});
}
}
到目前为止一切顺利?请注意,每个服务都可以接受它自己的特定搜索类作为输入参数。例如。 MamalService
只能接受MamalSearchOptions
类。
这是工厂......
public class AnimalFactory
{
private IDictionary<string, IAnimalService<IAnimal, IAnimalSearchOptions>> _animals;
public AnimalFactory()
{
_animals = new Dictionary<string, IAnimalService<IAnimal, IAnimalSearchOptions>>
{
{"Cat", new MamalService() },
{"Dog", new MamalService() },
{"GoldFish", new FishService() }
};
}
public IAnimalService<IAnimal, IAnimalSearchOptions> GetAnimalService(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
return _animals[name];
}
}
这是错误信息:
它不喜欢我试图将服务插入(通用?)字典。
现在,其他类和接口是什么样的?它们可以是found in this gist, here,而不是发送垃圾邮件。 &lt; - 完全100%回购。
有人可以解释我做错了什么并建议我需要做些什么吗?我一直认为,如果超类继承自泛型集合的Type,那么它们总是可以插入到泛型集合中。
答案 0 :(得分:4)
仅当IAnimalService
协变(例如声明为IAnimalService<out TAnimal, out TSearchOptions>
)时才会有效。
不幸的是,根据您的工厂界面,这似乎不起作用。例如,您有:
public class MamalService : IAnimalService<MamalItem, MamalSearchOptions>
{
public SearchResult<MamalItem> SearchForAnimals(MamalSearchOptions searchOptions)
{
throw new NotImplementedException();
}
}
public class FishService : IAnimalService<Fish, FishSearchOptions>
{
public SearchResult<Fish> SearchForAnimals(FishSearchOptions searchOptions)
{
throw new NotImplementedException();
}
}
如果编译器允许您的代码,可能会发生以下情况:
var dict = new Dictionary<string, IAnimalService<IAnimal, IAnimalSearchOptions>> {
{ "Dog", new MammalService() }
};
IAnimalService<IAnimal, IAnimalSearchOptions> service = dict["Dog"];
// this would be legal given the type of service, but would clearly not
// be correct for MammalService:
IAnimal animal = service.SearchForAnimals(new FishSearchOptions());
为了使这个设计起作用,您可以考虑让IAnimalService
仅将IAnimalSearchOptions
作为其参数而不是通用参数。然后你可以将TAnimal标记为out参数,你就是协变的。当然,您也会失去类型安全性,因为您的MammalService
必须将输入IAnimalSearchOptions
投射到MammalSearchOptions
。
另一个常见的解决方案是拥有2个版本的接口,一个是泛型的,一个不是(如IEnumerable和IEnumerable),非泛型版本放弃了一些类型安全性,但在上面显示的情况下更有用:
interface IAnimalService { IAnimal SearchForAnimals(IAnimalSearchOptions opts); }
interface IAnimalService<out TAnimal, in TOptions> : IAnimalService
where TAnimal : IAnimal
where TOptions : IAnimalSearchOptions {
TAnimal SearchForAnimals(TOptions opts);
}
class MammalService : IAnimalService<Mammal, MammalSearchOptions>
{
Mammal SearchForAnimals(MammalSearchOptions opts) { ... }
IAnimal IAnimalService.SearchForAnimals(IAnimalSearchOptions opts)
{
return this.SearchForAnimals((MammalSearchOptions)opts);
}
}
...
var dict = new Dictionary<string, IAnimalService> { "Dog", new MammalService() };