Я хочу заменить виртуальный метод производным типом класса. Как это лучше всего сделать в настоящее время? Пока что я нашел два подхода:
- Используйте
abstract
базуclass
для каждого производного типа; мост с помощьюprotected
методов. - Используйте
protected
реализацию сpublic
средством доступа.
Базовый случай (решение не реализовано, Clone
всегда возвращает базовый тип A1
):
public class A1
{
public int X1 { get; set; }
public A1(int x1) { this.X1 = x1; }
public virtual A1 Clone() { return new A1(X1); }
}
public class A2 : A1
{
public int X2 { get; set; }
public A2(int x1, int x2) : base(x1) { this.X2 = x2; }
public override A1 Clone() { return new A2(X1, X2); } //can't explicitly return A2
}
public class A3 : A2
{
public int X3 { get; set; }
public A3(int x1, int x2, int x3) : base(x1, x2) { this.X3 = x3; }
public override A1 Clone() { return new A3(X1, X2, X3); } //can't explicitly return A3
}
Решение №1 (использование abstract
базовых классов для каждого производного типа с protected
мостами):
public class B1
{
public int X1 { get; set; }
public B1(int x1) { this.X1 = x1; }
public virtual B1 Clone() { return new B1(X1); }
}
public abstract class B2_Base : B1
{
public B2_Base(int x1) : base(x1) { }
public sealed override B1 Clone() { return this.CloneAsB1(); }
protected abstract B1 CloneAsB1();
}
public class B2 : B2_Base
{
public int X2 { get; set; }
public B2(int x1, int x2) : base(x1) { this.X2 = x2; }
protected sealed override B1 CloneAsB1() { return this.Clone(); }
public new virtual B2 Clone() { return new B2(X1, X2); } //CAN explicitly return B2
}
public abstract class B3_Base : B2
{
public B3_Base(int x1, int x2) : base(x1, x2) { }
public sealed override B2 Clone() { return this.CloneAsB2(); }
protected abstract B2 CloneAsB2();
}
public class B3 : B3_Base
{
public int X3 { get; set; }
public B3(int x1, int x2, int x3) : base(x1, x2) { this.X3 = x3; }
protected sealed override B2 CloneAsB2() { return this.Clone(); }
public new virtual B3 Clone() { return new B3(X1, X2, X3); } //CAN explicitly return B3
}
Решение №2 (с использованием реализации protected
с public
аксессорами):
public class C1
{
public int X1 { get; set; }
public C1(int x1) { this.X1 = x1; }
public C1 Clone() { return this.CloneImplementation(); }
protected virtual C1 CloneImplementation() { return new C1(X1); }
}
public class C2 : C1
{
public int X2 { get; set; }
public C2(int x1, int x2) : base(x1) { this.X2 = x2; }
public new C2 Clone() { return this.CloneImplementation() as C2; } //trusts CloneImplementation to return a C2
protected override C1 CloneImplementation() { return new C2(X1, X2); }
}
public class C3 : C2
{
public int X3 { get; set; }
public C3(int x1, int x2, int x3) : base(x1, x2) { this.X3 = x3; }
public new C3 Clone() { return this.CloneImplementation() as C3; } //trusts CloneImplementation to return a C3
protected override C1 CloneImplementation() { return new C3(X1, X2, X3); }
}
Насколько я могу судить, Решение № 1 является наиболее строгим подходом, но для него требуется abstract
базовый class
для каждого производного class
, который хочет заменить возвращаемый базовый class
тип.
Решение №2 проще и понятнее, но в нем есть небольшой перерыв во внутренней безопасности типов. В частности, метод доступа public
каждого производного типа полагает, что его метод protected
вернет правильный тип. Таким образом, возможно отключение внутреннего типа, например:
public class C2 : C1
{
public int X2 { get; set; }
public C2(int x1, int x2) : base(x1) { this.X2 = x2; }
public new C2 Clone() { return this.CloneImplementation() as C2; } //trusts CloneImplementation to return a C2
protected override C1 CloneImplementation() { return new C1(X1); }
}
Есть ли правильная (общепринятая) передовая практика для переопределения методов производными типами?