Указание типа возврата абстрактного метода из базового класса в соответствии с подклассом

У меня есть следующая структура:

abstract class Base {
        public abstract List<...> Get(); //What should be the generic type?
}

class SubOne : Base {
    public override List<SubOne> Get() {

    }
}

class SubTwo : Base {
    public override List<SubTwo> Get() {

    }
}

Я хочу создать абстрактный метод, который возвращает любой класс, к которому относится конкретный подкласс. Итак, как видно из примера, метод в SubOne должен вернуться List<SubOne> тогда как метод в SubTwo должен вернуться List<SubTwo>,

Какой тип указывать в подписи, объявленной в базовом классе?


[ОБНОВИТЬ]

Спасибо за опубликованные ответы.

Решение состоит в том, чтобы сделать абстрактный класс универсальным, например:

abstract class Base<T> {
        public abstract List<T> Get();
}

class SubOne : Base<SubOne> {
    public override List<SubOne> Get() {

    }
}

class SubTwo : Base<SubTwo> {
    public override List<SubTwo> Get() {

    }
} 

5 ответов

Решение

Ваш абстрактный класс должен быть универсальным.

abstract class Base<T> {
        public abstract List<T> Get(); 
}

class SubOne : Base<SubOne> {
    public override List<SubOne> Get() {

    }
}

class SubTwo : Base<SubTwo> {
    public override List<SubTwo> Get() {
    }
}

Если вам нужно сослаться на абстрактный класс без аргумента универсального типа, используйте интерфейс:

interface IBase {
        //common functions
}

abstract class Base<T> : IBase {
        public abstract List<T> Get(); 
}
public abstract class Base<T> 
{       
    public abstract List<T> Get(); 
}

class SubOne : Base<SubOne> 
{
    public override List<SubOne> Get() { return new List<SubOne>(); }
}

class SubTwo : Base<SubTwo> 
{
    public override List<SubTwo> Get() { return new List<SubTwo>(); }
}

Попробуй это:

public abstract class Base<T> {
  public abstract List<T> Foo();
}

public class Derived : Base<Derived> {   // Any derived class will now return a List of
  public List<Derived> Foo() { ... }     //   itself.
}

Я не думаю, что вы можете сделать его конкретным подклассом. Вы можете сделать это, хотя:

abstract class Base<SubClass> {
        public abstract List<SubClass> Get(); 
}
class SubOne : Base<SubOne> {
    public override List<SubOne> Get() {
        throw new NotImplementedException();
    }
}
class SubTwo : Base<SubTwo> {
    public override List<SubTwo> Get() {
        throw new NotImplementedException();
    }
}

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

      abstract class Base {
}
interface IHasGetter<T> {
  List<T> Get();
}
static class HasGetterHelper {
    public static List<T> Get<T>(this T v) where T: IHasGetter<T>  => v.Get();
}
class SubOne : Base, IHasGetter<SubOne> {
    public List<SubOne> Get() {
        throw new NotImplementedException();
    }
}
class SubTwo : Base, IHasGetter<SubTwo> {
    public List<SubTwo> Get() {
        throw new NotImplementedException();
    }
}

Если вы не можете добавить интерфейс к своим подклассам и по-прежнему не можете добавить универсальные шаблоны к базовому типу, этот метод может быть полезен: (К сожалению, вы не можете сделать GetImpl защищенным, поскольку вспомогательному классу не разрешено находиться внутри базового класса )

      abstract class Base {
    public abstract List<T> GetImpl<T>() where T:Base;
}
static class HasGetterHelper {
    public static List<T> Get<T>(this T v) where T: Base => v.GetImpl<T>();
}
class SubOne : Base {
    public override List<T> GetImpl<T>() {
        throw new NotImplementedException();
    }
}
class SubTwo : Base {
    public override List<T> GetImpl<T>() {
        throw new NotImplementedException();
    }
}

В обоих случаях это будет работать должным образом:

      class Tester
{
   List<SubOne> TestIt() => new SubOne().Get();
}
Другие вопросы по тегам