Создайте экземпляр производного класса из базового класса

У меня есть абстрактный базовый класс A:

public abstract class A : ICloneable {

    public int Min { get; protected set; }
    public int Max { get; protected set; }

    public A(int low, int high)
    {
        this.Min = low;
        this.Max = high;
    }

    //...

    public object Clone()
    {
        return new this(this.Min, this.Max); //<-- ??
    }
}

Который расширен моим классом B:

public class B : A
{
    public B(int low, int high) : base(low, high) { }

    //...
}

Поскольку A является абстрактным, он не может быть создан, а производный класс может. Можно ли из класса A создать новый экземпляр класса B?

Предположим, что класс A имеет много производных классов, как он узнает, какой из них нужно создать?

Что ж, я хочу создать экземпляр того же класса (или типа), что и мой текущий A.

То есть, если я вызываю метод Clone из класса B, я хочу создать новый экземпляр B. Если я вызываю метод Clone из класса C, я хочу создать экземпляр нового C.

Мой подход состоял в том, чтобы написать что-то вроде:

return new this(this.Min, this.Max);

Но это, похоже, не работает и не компилируется.

Можно ли это сделать в C#?

Если это не так, есть ли объяснение, чтобы я мог понять?


person Matias Cicero    schedule 06.08.2014    source источник
comment
Это не отвечает на ваш вопрос, но почему вы вместо этого не используете this.MemberwiseClone()?   -  person Ondrej Svejdar    schedule 06.08.2014
comment
Я не знал о методе MemberwiseClone(), но я просто хочу, чтобы копия была новым экземпляром с теми же аргументами конструктора. Любая другая обработка, которую может выполнить MemberwiseClone, будет бесполезной.   -  person Matias Cicero    schedule 06.08.2014
comment
Что, если конструктор B добавит еще один параметр?   -  person Sriram Sakthivel    schedule 06.08.2014
comment
Я надеюсь, что этого не произойдет   -  person Matias Cicero    schedule 06.08.2014


Ответы (3)


Хотя мне нравится решение Jamiec, мне не хватает грязного решения с использованием отражения :)

public class A {
  public object Clone() {
    var type = GetType().GetConstructor(new[] { typeof(int), typeof(int) });
    return type.Invoke(new object[] { this.Min, this.Max });
  }
}
person Ondrej Svejdar    schedule 06.08.2014
comment
+1 за очевидное решение для отражения, которое работает с непараметрическими кторами - person Jamiec; 06.08.2014
comment
Оооо, грязно, мне нравится :D - person Matias Cicero; 06.08.2014

Да, это возможно с помощью абстрактного фабричного метода в вашем базовом классе.

public abstract class A
{
   public int Min { get; protected set; }
   public int Max { get; protected set; }

   public A(int low, int high)
   {
       this.Min = low;
       this.Max = high;
   }
   protected abstract A CreateInstance(int low, int high);

   public object Clone()
   {
      return this.CreateInstance(this.Min,this.Max);
   }
}

public class B:A
{
   public B(int low, int high)
      : base(low,high)
   {
   }
   protected override A CreateInstance(int low, int high)
   {
      return new B(low,high);     
   }
}
person Jamiec    schedule 06.08.2014
comment
Это решение тоже пришло мне в голову, но я с нетерпением ждал возможности не добавлять код построения в каждый производный класс, а позволить C# сделать это за меня, создав экземпляр любого производного класса, используемого для расширения A. Надеюсь, я достаточно хорошо объясняю свои мысли. - person Matias Cicero; 06.08.2014
comment
@MatiCicero - это выполнимо, но вам придется много размышлять. Подумайте, подходит ли вам это решение - person Jamiec; 06.08.2014
comment
Я проверю оба ответа и вернусь с выводом. Спасибо - person Matias Cicero; 06.08.2014

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

public abstract class A
{
    public abstract A Clone( );
}

public class B : A
{
    public override A Clone( )
    {
        return new B( );
    }
}

public class C : A
{
    public override A Clone( )
    {
        return new C( );
    }
}

Поскольку вы используете С#, вы можете использовать класс Activator. Вы можете сделать метод Clone виртуальным (не === абстрактным) с реализацией по умолчанию.

public abstract class A
{
    public virtual A Clone( )
    {
        // assuming your derived class contain a default constructor.
        return (A)Activator.CreateInstance(this.GetType( ));
    }
}

Изменить. Если у вас нет конструктора по умолчанию без параметров во всех ваших производных классах, вы можете добавить параметры в метод Activator.CreateInstance.

(A)Activator.CreateInstance(this.GetType( ), this.Min, this.Max);

Для различных конструкторов производных типов я бы рекомендовал вам переопределить метод Clone специально для этих типов вместо использования реализации Clone по умолчанию.

person rtlayzell    schedule 06.08.2014
comment
В этом последнем примере ответ должен быть приведен к A, как в return (A)Activator.CreateInstance(this.GetType( ));, поскольку Activator.CreateInstance возвращает объект - person Jamiec; 06.08.2014
comment
Это именно то, что я хотел, я протестирую это и вернусь с новостями - person Matias Cicero; 06.08.2014
comment
Проблема с этим решением заключается в том, что оно полагается на конструктор без параметров, которого нет у OP. Для решения для отражения, которое работает с вашим конструктором, посмотрите на другой ответ @MatiCicero - person Jamiec; 06.08.2014
comment
Ничто не мешает вам добавить параметры в метод Activator.CreateInstance. Я решил опустить их, потому что конструкторы производных классов неизвестны в области A (см. редактирование для продолжения..) - person rtlayzell; 06.08.2014