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

Имам своя абстрактен базов клас 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 за очевидното решение за отражение, което работи с безпараметрични ctors - 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( );
    }
}

Тъй като използвате 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