简体   繁体   English

泛型与继承:我在这里做错了什么?

[英]Generics & Inheritance: What am I doing wrong here?

There are the following 4 objects declared: 声明了以下4个对象:

abstract class AConfigAction {}

abstract class APlugin<ConfigActionType> where ConfigActionType :AConfigAction {}

class AppExecuteConfigAction : AConfigAction {}

class AppExecutePlugin : APlugin<AppExecuteConfigAction>{}
  • All classes are public. 所有课程都是公开的。 Bodies have been removed for simplicity. 为简单起见,已移除了实体。

Why this fails to be converted? 为什么这不能转换?

_plugins = new List<APlugin<AConfigAction>>();
_plugins.Add(new AppExecutePlugin());  <--- Error

cannot convert from 'AppExecutePlugin' to 'APlugin' 无法从'AppExecutePlugin'转换为'APlugin'


Full error message: 完整的错误消息:

Error 1 The best overloaded method match for 'System.Collections.Generic.List>.Add(EnvironmentSwitcher.Model.ConfigAction.APlugin)' has some invalid arguments R:\\projects\\EnvironmentSwitcher\\EnvironmentSwitcher\\View\\ConfigurationActionManagerForm.cs 35 错误1'System.Collections.Generic.List> .Add(EnvironmentSwitcher.Model.ConfigAction.APlugin)'的最佳重载方法匹配有一些无效参数R:\\ projects \\ EnvironmentSwitcher \\ EnvironmentSwitcher \\ View \\ ConfigurationActionManagerForm.cs 35

Error 2 Argument '1': cannot convert from 'EnvironmentSwitcher.Model.ConfigAction.AppExecute.AppExecutePlugin' to 'EnvironmentSwitcher.Model.ConfigAction.APlugin' R:\\projects\\EnvironmentSwitcher\\EnvironmentSwitcher\\View\\ConfigurationActionManagerForm.cs 35 错误2参数'1':无法从'EnvironmentSwitcher.Model.ConfigAction.AppExecute.AppExecutePlugin'转换为'EnvironmentSwitcher.Model.ConfigAction.APlugin'R:\\ projects \\ EnvironmentSwitcher \\ EnvironmentSwitcher \\ View \\ ConfigurationActionManagerForm.cs 35

Let's make that a bit easier to understand: 让我们更容易理解:

abstract class Animal {} // was AConfigAction
abstract class Cage<T> where T : Animal {} // was APlugIn
class Tiger : Animal {} // was AppExecuteConfigAction
class TigerCage : Cage<Tiger>{} // was AppExecutePlugin

var cages = new List<Cage<Animal>>();    
cages.Add(new TigerCage()); // Why is this an error?

Suppose that were legal. 假设这是合法的。 What stops this? 什么阻止了这个?

class Shark : Animal {} // some other config action
...
var cages = new List<Cage<Animal>>();    
cages.Add(new TigerCage()); 
Cage<Animal> firstCage = cages[0]; 
firstCage.InsertIntoCage(new Shark());

firstCage is of type Cage<Animal> which implies that it can hold any kind of animal. firstCage属于Cage<Animal>类型,暗示它可以容纳任何种类的动物。 But in fact we know that it is a tigers-only cage. 但实际上我们知道这只是一只只有老虎的笼子。 You just put a shark into a tiger cage, which seems uncomfortable for both the shark and the tiger. 你只是将鲨鱼放入虎笼中,这对鲨鱼和老虎来说都是不舒服的。

Obviously that cannot be allowed. 显然不能允许。 What prevents it? 是什么阻止了它? The only thing that prevents it is that it is illegal to put a tiger cage into a collection of animal cages in the first place. 唯一能阻止它的是,首先将虎笼放入动物笼子的集合中是违法的。 A tiger cage is not a kind of animal cage because there are things you can do with an animal cage that you cannot do with a tiger cage, namely, put a shark into it. 虎笼不是一种动物笼子,因为你可以用动物笼子做一些你不能用虎笼做的事情,就是把鲨鱼放进去。 A basic principle of object-oriented design is that subtypes can do everything their supertypes can do; 面向对象设计的基本原则是子类型可以完成超类型可以做的所有事情; a tiger cage cannot do everything an animal cage can do, so it is not a subtype. 老虎笼不能做动物笼子可以做的所有事情,所以它不是一个亚型。

The more highfalutin way to say this is that generic types cannot be made to be covariant in their type arguments because doing so would violate the Liskov Substitution Principle . 更高调的方式是说, 泛型类型不能在其类型参数中变成协变,因为这样做会违反Liskov替换原则 In C# 4, certain interfaces and delegates are covariant in their type arguments. 在C#4中,某些接口和委托在其类型参数中协变的。 For example, it is legal in C# 4 to put an IEnumerable<Tiger> into a List<IEnumerable<Animal>>> because there's no way that this can be made unsafe. 例如,在C#4中将IEnumerable<Tiger>放入List<IEnumerable<Animal>>> IEnumerable<Tiger>是合法的,因为没有办法使它不安全。 We can uphold the substitution principle while allowing covariance because IEnumerable<T> is an "out-only" interface. 我们可以在允许协方差的同时维护替换原则,因为IEnumerable<T>是一个“out-only”接口。 You only ever take tigers out; 你只带老虎出去; there's no way to put sharks in. 没有办法把鲨鱼放进去。

Generics covariance and contravariance is supported in C# 4.0. C#4.0支持泛型协方差和逆变 It works with interfaces though and not abstract classes: 它适用于接口,而不是抽象类:

abstract class AConfigAction { }

interface APlugin<out ConfigActionType> where ConfigActionType : AConfigAction { }

class AppExecuteConfigAction : AConfigAction { }

class AppExecutePlugin : APlugin<AppExecuteConfigAction> { }

class Program
{
    public static void Main()
    {
        var _plugins = new List<APlugin<AConfigAction>>();
        _plugins.Add(new AppExecutePlugin());
    }
}

In C# 3.5 this is not supported. 在C#3.5中,这不受支持。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM