Generics & Inheritance: что я здесь не так делаю? - PullRequest
4 голосов
/ 31 декабря 2011

Объявлены следующие 4 объекта:

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 \ EnvironmentSwitcher \ View \ ConfigurationActionManagerForm.cs 35

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

Ответы [ 2 ]

22 голосов
/ 31 декабря 2011

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

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> является интерфейсом «только для использования».Вы только когда-либо убиваете тигров;туда нельзя посадить акул.

4 голосов
/ 31 декабря 2011

Общая ковариация и контравариантность поддерживается в 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 это не поддерживается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...