Переопределение виртуального метода с общим типом возвращаемого значения

#c# #generics

#c# #общие

Вопрос:

Рассмотрим эти два абстрактных класса. MyClass2 расширяет мой базовый класс и переопределяет виртуальный метод. Обратите внимание, что он также определяет TValue MyBaseClass как IList…

 public abstract class MyBaseClass<TKey, TValue>
{
    public virtual IList<TValue> DoSomeStuff()
    {
        IList<TValue> resu<
        ....
        return restu<
    }
}

public abstract class MyClass2<TKey, TValue>: MyBaseClass<TKey, IList<TValue>>
{
    public override IList<TValue> DoSomeStuff()
    {
        IList<TValue> resu<
        ....
        return restu<
    }
}
  

Этот код не компилируется. Жалоба касается возвращаемого типа MyClass2.doSomeStuff. Ошибка «Не удается изменить тип возвращаемого значения при переопределении метода» IList<TValue> MyClass2<TKey, TValue>.DoSomeStuff() «

Я не совсем понимаю, почему это неправильно. Почему бы компилятору или .net не считать, что значение переопределенного метода равно значению MyClass2?

Ответ №1:

Проблема в вашей части базового класса:

 public abstract class MyClass2<TKey, TValue>: MyBaseClass<TKey, IList<TValue>>
  

Замените IList<TValue> на TValue , и это сработает — метод уже возвращает IList .

Ответ №2:

TValue Параметр вашего базового класса, унаследованный вашим подклассом, фактически IList<TValue> соответствует объявленному вашим подклассом. Внутренний TValue относится к вашему подклассу.

Итак, если вы пытаетесь переопределить существующий виртуальный метод, который принадлежит базовому классу, на самом деле ожидается, что возвращаемый тип будет IList<IList<TValue>> ; то есть IList из TValue , который принадлежит вашему базовому классу, а не подклассу.

Комментарии:

1. хммм .. тогда, я думаю, единственный способ сохранить тип неизменным — это «обобщить» сам метод. обобщения и наследование, похоже, вызывают некоторые проблемы.

2. @mike01010 Из ограниченного кода, который вы показали, я бы изменил класс на MyClass2<TKey, TValue>: MyBaseClass<TKey, TValue> вместо этого, если вы хотите DoSomeStuff все еще возвращать IList<TValue>

Ответ №3:

Вы получаете ошибку, потому что TValue дочерний класс на самом деле IList<TValue> находится в базовом классе, поэтому сигнатура метода (как ее видит ваш базовый класс) будет IList<IList<TValue>> которая, очевидно, не соответствует сигнатуре виртуального метода.

Чтобы получить соответствующие типы, вам придется изменить подпись:

 public override TValue DoSomeStuff()
{
    TValue resu<

    return resu<
}
  

Комментарии:

1. Вместо этого я бы изменил сигнатуру виртуального метода.

2. @BoltClock — Все зависит от того, какой из них на самом деле неправильный. Я предполагал, что у базового класса будут другие дочерние элементы, которые могут иметь совершенно разные реализации. В этом случае вам пришлось бы изменить дочерний метод.

3. TKey и TValue используются для словаря. В некоторых классах словарь хранит единственное значение, в других — список (multimap). так, например, он может хранить {int, MyObject} или {int, IList<MyObject>}. Я предполагаю, что, возможно, я пытаюсь заставить эти классы делать слишком много в этом случае.