C # ковариация и наследование - PullRequest
7 голосов
/ 09 июня 2011

Мне любопытно узнать, почему реализация моего интерфейса в абстрактном базовом классе не удовлетворяет требованиям подклассов. Вот пример:

public interface IBase { }
public interface IConcrete : IBase { }

public interface IBaseManager<out T>
    where T : IBase
{
    T Create();
    IEnumerable<T> SelectAll();
}

public interface IConcreteManager : IBaseManager<IConcrete> { }

public abstract class Base : IBase { }

public class Concrete1 : Base, IConcrete { }

public abstract class BaseManager<T> : IBaseManager<T> where T : class, IBase
{
    #region IBaseManager<T> Members

    public T Create()
    {
        throw new NotImplementedException();
    }

    public IEnumerable<T> SelectAll()
    {
        throw new NotImplementedException();
    }

    #endregion
}

public class ConcreteManager : BaseManager<Concrete>, IConcereteManager
{
             //error occurs here
} 

Это генерируемая ошибка:

'ConsoleApplication4.ConcreteManager' не реализует элемент интерфейса 'ConsoleApplication4.IBaseManager .Create ()'.

«ConsoleApplication4.BaseManager .Create ()» не может реализовать «ConsoleApplication4.IBaseManager .Create ()», поскольку у него нет соответствующего возвращаемого типа «ConsoleApplication4.IConcrete».

Если я добавлю эти методы в класс ConcreteManager, все будет хорошо, и компилятор будет счастлив.

public new IConcrete Create()
{
    return base.Create();
}

public new IEnumerable<IConcrete> SelectAll()
{
    return base.SelectAll();
}

Если достаточно просто вернуть то, что возвращают методы из базового класса, зачем добавлять методы? Почему компилятор не может вызывать методы базового класса?

Ответы [ 3 ]

7 голосов
/ 09 июня 2011

Как правильно указывает Джон, язык C # не поддерживает ковариацию возвращаемого типа.Так же как и CLR, поэтому, даже если язык поддерживал его, единственный способ, которым мы могли бы реализовать функцию, состоял бы в том, чтобы молча генерировать именно тот код, который вы должны были добавить самостоятельно.

Небольшое преимущество, которое дают разработчикам возможность избежать написания этих методов-заглушек, на самом деле не оправдывает значительных затрат на выполнение более общей ковариационной функции, поэтому мы никогда не делали этого.

5 голосов
/ 09 июня 2011

Похоже, вы предполагаете ковариацию возвращаемого типа, поскольку ConcreteManager (как IConcreteManager) ожидает оба метода Create() и SelectAll() с типом возврата IConcrete и IEnumerable<IConcrete> соответственно, чтобазовый класс не предоставляет.

Вы получаете эти ошибки, потому что C # не поддерживает ковариацию возвращаемого типа.

1 голос
/ 09 июня 2011

Когда вы реализуете интерфейс / абстрактный класс, вы должны использовать одну и ту же сигнатуру. Смотрите здесь

Не позволяйте дженерикам сбить вас с толку, это ничем не отличается от того, если бы не было дженериков.

...