Использовать производный тип в базовом абстрактном классе

Хорошо, у меня есть несколько разных классов, производных от базового класса. Этот базовый класс является абстрактным, содержащим методы commom.

Один из методов - это метод Copy, который должен присутствовать во всех производных классах, поэтому я поместил его в базовый класс. НО, я хочу, чтобы он возвращал derived type не базу или объект.

Решение, которое я получил для этого, использует параметр типа:

abstract class CopyableClass<T>
{
    public abstract T Copy();
}

class DerivedClass : CopyableClass<DerivedClass>
{
    public override DerivedClass Copy()
    {
        //do what is needed for copy and return a new DerivedClass
    }
}

Итак, основная цель здесь -

Удалите параметр типа в базовом классе и по-прежнему заставьте метод возвращать соответствующий производный тип.


Одно решение.

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

abstract class BaseClass
{
    //base methods not related to deriving type
}

interface ICopyable<T>
{
     T Copy();
}

class DerivedClass : BaseClass, ICopyable<DerivedClass>
{
    public DerivedClass Copy()
    {
        //do what is needed for copy and return a new DerivedClass
    }
}

person Daniel Möller    schedule 02.07.2013    source источник
comment
Так в чем твой вопрос?   -  person evanmcdonnal    schedule 02.07.2013
comment
может быть лучше реализовать это как общий интерфейс?   -  person Guru Stron    schedule 02.07.2013
comment
как удалить параметр типа в базовом классе и по-прежнему заставить метод возвращать соответствующий производный тип.   -  person Daniel Möller    schedule 02.07.2013
comment
@Daniel, вам следует создать один универсальный метод в базовом классе, а не определять его во всех производных классах. Я дам ответ на примере метода, который работает вот так.   -  person evanmcdonnal    schedule 02.07.2013
comment
Это интересно ... есть ли способ ограничить параметр типа только производным типом?   -  person Daniel Möller    schedule 02.07.2013
comment
@ Дэниел, да, сэр. Это в моем ответе.   -  person evanmcdonnal    schedule 02.07.2013
comment
@ Дэниел, да, конечно abstract class CopyableClass<T>:where T:CopyableClass{}   -  person Guru Stron    schedule 02.07.2013
comment
Это предложение ограничивается базовым типом, а не производным типом. (Боюсь, мне нужны невозможные вещи, но для абстрактных классов было бы очень хорошо использовать общий производный тип)   -  person Daniel Möller    schedule 02.07.2013


Ответы (4)


Вы действительно не можете. Базовый класс не может знать все будущие реализации. Вам придется прибегнуть к типу универсального абстрактного класса (как и вы) или универсальному методу Copy.

public abstract class CopyableClass
{
    public abstract T Copy<T>() where T : CopyableClass;
}

public class DerivedClass : CopyableClass
{
    public override T Copy<T>()
    {
        if(typeof(T) != typeof(DerivedClass))
            throw new ArgumentException();

        // return your copy
    }
}

Или, если вы хотите обобщить проверку типа в своем базовом классе:

public abstract class CopyableClass
{
    public T Copy<T>() where T : CopyableClass
    {
        if(GetType() != typeof(T))
            throw new ArgumentException();

        return (T) Copy();
    }

    protected abstract CopyableClass Copy();
}

public class DerivedClass : CopyableClass
{
    protected override CopyableClass Copy()
    {
        return // Your copy;
    }
}

Обратите внимание, что второй метод дает много доверия реализации производного класса, поскольку он слепо приводит возвращаемое значение абстрактного метода. Компилятор позволит вам вернуть другой тип, реализующий CopyableClass, в производном типе, но это будет ошибка времени выполнения. Это не проблема, если у вас есть абсолютный контроль над всеми производными реализациями (т.е. ваш абстрактный класс также имеет внутренний конструктор).

person Simon Belanger    schedule 02.07.2013

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

    public abstract class BaseClass
    {
    }

    public abstract class CopyableClass<T> : BaseClass
        where T: BaseClass, new()
    {
        public T Copy()
        {
            var copy = new T(); // Creating a new instance as proof of concept

            return copy;
        }
    }

    public class DerivedClass : CopyableClass<DerivedClass>
    {
    }
person K Ekegren    schedule 16.11.2017

Вы действительно хотите реализовать копию в базовом классе и вернуть T. Это приведет к тому, что вы вызовете его с аргументом типа, и он вернет этот тип.

public static T Copy<T>() where T : CopyableClass
{
    T retVal = new T();
    // do whatever copying is required
    return retVal;
}

Чтобы назвать это, вы делаете;

DerivedClass d = Copy<DerivedClass>();

Ваш код для фактического копирования может потребовать немного больше работы, чтобы сделать его универсальным, но это стоит усилий, поскольку у вас будет единственная реализация Copy(), которая работает для любого производного типа. Я не знаю, какая логика принадлежит этому методу, поэтому я просто заглушил все. Кроме того, я бы рекомендовал проверить дженерики в целом. Часто они являются лучшим вариантом для подобных вещей. Если ваши реализации должны быть уникальными для базового класса, сохраните то же определение метода, но сделайте его абстрактным, а затем переопределите его в базовых классах.

person evanmcdonnal    schedule 02.07.2013

Это позволит вам преобразовать этот базовый класс в производный тип и вернуть его.

public abstract class BaseClass<TDerived> : where TDerived: BaseClass<TDerived>
{
   public TDerived DoSomethingCommon(string param)
   {
      var derivedType = (TElement)this;
      //do something.
      return derivedType;
   }
}
person Siegeon    schedule 07.03.2014