我有一个基类,有很多子类。这个基类定义了一些只读属性和变量,并给它们赋予默认值。因为每个子类的情况可能不同,所以这些默认值可能会不同。
只读属性/字段允许你在构造函数内和定义时更改变量的值,但是不能在其他地方更改。如果我尝试在子类的构造函数中更改继承的只读变量的值,就会出现“只读变量只能在构造函数中分配”的错误。为什么会这样?如何避免此问题,而不使用反射?
我的意图是:通过脚本允许用户进行可扩展性,使他们只能更改某些字段一次。
我有一个基类,有很多子类。这个基类定义了一些只读属性和变量,并给它们赋予默认值。因为每个子类的情况可能不同,所以这些默认值可能会不同。
只读属性/字段允许你在构造函数内和定义时更改变量的值,但是不能在其他地方更改。如果我尝试在子类的构造函数中更改继承的只读变量的值,就会出现“只读变量只能在构造函数中分配”的错误。为什么会这样?如何避免此问题,而不使用反射?
我的意图是:通过脚本允许用户进行可扩展性,使他们只能更改某些字段一次。
当一个字段声明包含readonly修饰符时,通过该声明引入的字段的赋值仅可以作为声明的一部分或在同一类的构造函数中发生。
为了解决这个问题,您可以在基类中创建一个受保护的构造函数,该构造函数需要带有readonly属性的参数。
例如:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Base b = new Child();
Console.WriteLine(b.i);
Console.Read();
}
}
class Base
{
public readonly int i;
public Base()
{
i = 42;
}
protected Base(int newI)
{
i = newI;
}
}
class Child : Base
{
public Child()
: base(43)
{}
}
}
通过使用虚拟的只读属性,您可以获得您所期望的精确行为。
public class BSE
{
virtual public int Prop
{
get
{
return 6;
}
}
}
public class Derived : BSE
{
public override int Prop
{
get
{
return 10;
}
}
}
字段在继承和重载模型之外,不应该用来提供多态特性。
public readonly int number = 5
相比,仍然是一个相当大的跳跃。 - Steffan DonalAdam给出了正确的答案。如果你担心构造函数中参数数量会占用太多空间,那么你应该把这个问题作为一个独立问题考虑,并采用不同的解决方案:创建一个BaseConfig类,其中包含所有这些属性,这是唯一需要传递的内容。Base可以从BaseConfig的属性分配所有只读字段,或者您可以让Base仅持有一个BaseConfig类型的只读字段,并引用它来获取值。
至于为什么要这样做,请参见C# constructor execution order,了解每个类的只读字段何时被初始化或可初始化的情况。
您可以使用具有公共get访问器和受保护set访问器的属性。派生类可以设置此属性的值。
例如:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Base b = new Child();
Console.WriteLine(b.I);
Console.Read();
}
}
class Base
{
public int I { get; protected set; }
public Base()
{
I = 42;
}
}
class Child : Base
{
public Child()
{
I = 43;
}
}
}
protected
不是只读的。派生类可以在其构造函数之外设置它。 - joe这是设计上不可能的。尝试将值传递给一个protected
基类构造函数。
abstract
代替virtual
:protected abstract class BaseClass
{
protected abstract int PrivateI { get; }
}
public class DerivedClass : BaseClass
{
private readonly int _i;
protected override int PrivateI => _i;
public DerivedClass(int i)
{
_i = i;
}
}