在C#中实现父类可调用的抽象类属性重写的最优方案
在C#中实现子类重写父类属性的更优方案
在不少编程语言里,继承抽象类时可以直接重写类的属性字段,父类方法调用时会自动读取子类的新值,示例如下:
abstract class A { protected string foo; void PrintFoo() { print(foo); } } class B extends A { protected string foo = "bar"; } (new B()).PrintFoo(); // outputs: bar
但这种写法在C#里无法直接实现,下面先梳理已尝试的几种方案,再讨论更优实现:
已尝试的实现方案
方法一:构造函数传参
通过子类构造函数向父类构造函数传递参数初始化字段,代码如下:
abstract class A { protected string foo; public A(string foo) { this.foo = foo; } void PrintFoo() { Console.WriteLine(foo); } } class B : A { public B() : base("bar") {} } (new B()).PrintFoo(); // outputs: bar
这种方式的缺点是不够简洁,每个子类都得在构造函数里显式传参。
方法二:虚属性重写
定义虚属性,子类重写属性的取值逻辑,代码如下:
abstract class A { protected virtual string Foo { get; } void PrintFoo() { Console.WriteLine(Foo); } } class B : A { protected override string Foo { get { return "bar"; } } } (new B()).PrintFoo(); // outputs: bar
这种方式更符合面向对象的多态设计,但写法还有简化空间。
简化版:表达式体属性
利用C#的表达式体语法进一步简化代码:
abstract class A { public virtual string Foo => null; } class B : A { public override string Foo => "bar"; }
更优的实现方式
如果追求极致简洁且贴合C#的设计习惯,表达式体的虚属性重写已经是当前比较优的方案,它兼顾了多态特性和代码简洁性。
另外,如果父类本身就要求子类必须提供该属性的值,可以把父类的属性定义为abstract,这样子类必须强制实现,语义更清晰,写法也更简洁:
abstract class A { protected abstract string Foo { get; } void PrintFoo() { Console.WriteLine(Foo); } } class B : A { protected override string Foo => "bar"; }
这种方式省去了父类的默认实现,还能避免子类忘记重写导致的默认值问题,完全贴合抽象类的设计初衷。
如果子类的属性值是编译期常量,也可以结合const使用,但要注意const是静态成员,不能用于虚属性重写;如果是实例级的固定值,表达式体属性已经足够简洁。
内容的提问来源于stack exchange,提问作者maduranma
相关产品推荐
相关产品推荐

