2011-12-31 4 views
4

Есть следующие 4 объекта объявлены:Generics & Inheritance: Что я здесь делаю неправильно?

abstract class AConfigAction {} 

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

class AppExecuteConfigAction : AConfigAction {} 

class AppExecutePlugin : APlugin<AppExecuteConfigAction>{} 
  • Все классы являются общедоступными. Для простоты тела были удалены.

Почему это не может быть преобразовано?

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

не может конвертировать из 'AppExecutePlugin' в '' APlugin


Полное сообщение об ошибке:

Ошибка 1 Лучший перегружен матч метод «System.Collections .Generic.List> .Add (EnvironmentSwitcher.Model.ConfigAction.APlugin) 'имеет некоторые недопустимые аргументы R: \ projects \ EnvironmentSwitcher \ Environmen tSwitcher \ Вид \ ConfigurationActionManagerForm.cs 35

Ошибка 2 Аргумент '1': не удается преобразовать из 'EnvironmentSwitcher.Model.ConfigAction.AppExecute.AppExecutePlugin' в R 'EnvironmentSwitcher.Model.ConfigAction.APlugin': \ Projects \ EnvironmentSwitcher \ EnvironmentSwitcher \ View \ ConfigurationActionManagerForm.cs 35

ответ

22

Давайте сделаем это немного легче понять:

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? 

Предположим, что были законными. Что это останавливает?

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 имеет тип Cage<Animal>, который предполагает, что он может содержать любой вид животного. Но на самом деле мы знаем, что это клетка только для тигров. Вы просто помещаете акулу в клетку тигра, которая кажется неудобной как для акулы, так и для тигра.

Очевидно, что это невозможно. Что мешает? Единственное, что его предотвращает, - это то, что в первую очередь помещать клетку тигра в коллекцию животных. Тигр-клетка не вид животного клетка, потому что есть вещи, которые вы можете сделать с клеткой для животных, которую вы не можете сделать не можете делать с клеткой тигра, а именно вставлять в нее акулу. Основным принципом объектно-ориентированного проектирования является то, что подтипы могут делать все, что могут сделать их супертипы; клетка тигра не может делать все, что может сделать клетка для животных, поэтому она не является подтипом.

Более высокий уровень вероятности сказать, что общие типы не могут быть ковариантными в аргументах своего типа, потому что это нарушит Принцип замещения Лискова. В C# 4 определенные интерфейсы и делегаты являются ковариантными в своих аргументах типа.Например, в C# 4 законно поставить IEnumerable<Tiger> в List<IEnumerable<Animal>>>, потому что это невозможно, так как это невозможно сделать опасным. Мы можем поддерживать принцип замещения, допуская ковариацию, потому что IEnumerable<T> - это интерфейс «только для выхода». Вы только когда-нибудь выбираете тигров; нет никакого способа поставить акул.

+2

+1 для приятного примера! – Odys

+0

Хорошая идея с аналогией в клетке ... :) – Lucero

+0

Если 'Cage ' является абстрактным классом, а не 'class AnimalCage: Cage {}' существует, как вы можете называть 'InsertIntoCage' на' firstCage'? – comecme

4

Generics covariance and contravariance поддерживается в C# 4.0. Он работает с интерфейсами, хотя и не абстрактными классами:

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()); 
    } 
} 

В C# 3.5 это не поддерживается.

+0

спасибо, Что означает 'out' в вашем ответе? – Odys

+0

@odyodyodys, он определяет общий аргумент 'ConfigActionType' как ковариантный, означающий, что члены внутри этого интерфейса могут только возвращать его, но не принимать в качестве аргумента. –

+0

Я полагаю, что это функция .Net 4 (как я понимаю из вашего ответа и ответа Эрика). – Odys