C# Setter 接口和继承接口的行为差异
C# Setter behavior difference in interface and interface with inheritance
我目前正在学习 C#,并且在玩耍时观察到无法解释的行为。
鉴于此界面:
using System;
namespace LearnInterfaces
{
interface IAutomobile
{
string LicensePlate { get; }
double Speed { get; }
int Wheels { get; }
void Honk();
}
}
我这样实现 class :
using System;
namespace LearnInterfaces
{
class Sedan : IAutomobile
{
public Sedan(double speed, int wheels){
this.Speed = speed;
this.LicensePlate = Tools.GenerateLicensePlate();
this.Wheels = wheels;
}
public string LicensePlate
{ get; }
public double Speed
{ get; }
public int Wheels
{ get; }
publc void Honk(){.....};
}
}
这很好,我可以在我的主程序中实例化 Sedan。
但是,当涉及到继承+接口时,这种行为就不行了:
如果我决定添加一个新的基础 class 并使 Sedan
继承它:
class Vehicle
{
public string LicensePlate
{ get; }
public double Speed
{ get; }
public int Wheels
{ get; }
public void Honk()
{
Console.WriteLine("HONK!");
}
}
已更改,以便 Sedan 从中继承:
class Sedan : Vehicle, IAutomobile
{
public Sedan(double speed)
{
Speed = speed;
LicensePlate = Tools.GenerateLicensePlate();
Wheels = 4;
}
}
现在,如果我尝试从我的主程序实例化,我会得到 Property or indexer 'Vehicle.Speed' cannot be assigned to -- it is read only
。 class.
中的每个 属性 都会发生同样的错误
当我让 Sedan 从基础 class 继承时,为什么现在需要 setter?据我了解,Sedan 也将继承 getter;所以用构造函数实例化它应该没问题(就像前面的例子一样,它只是实现了接口)。
这取决于您的约束条件和您想要实现的目标。但是,问题是因为您的基础 class 只获取属性。这些属性只能在构造函数中设置,不包括派生构造函数。实现此目的的最简单(可能不是最佳)方法是在基础 class 上使用 protected setter,这使得这些属性对于派生的 classes 是可变的。另一种方法是在你的基础上有构造函数 class
A protected member is accessible within its class and by derived class
instances.
public string LicensePlate { get; protected set; }
public double Speed { get; protected set; }
public int Wheels { get; protected set; }
Why do I need a setter now when I make Sedan
inherit from a base class? In my understanding, the Sedan
will also inherit the getter; So instantiate it with constructor should be ok (just like previous example where it simply implementes the interface).
在只有 get
的 C# 属性中,只能由它们自己的构造函数初始化,而不能由子 class 中的构造函数初始化。您需要向 Vehicle
添加一个构造函数,它接受 LicensePlate
、Speed
和 Wheels
的值,然后将这些值传递给父级 class'构造函数:
public class Vehicle
{
protected Vehicle( string licensePlate, double speed, int wheels )
{
this.LicensePlate = licensePlate;
this.Speed = speed;
this.Wheels = wheels;
}
// etc
}
public class Sedan : Vehicle, IAutomobile
{
public Vehicle( string licensePlate, double speed, int wheels )
: base( licensePlate, speed, wheels )
{
}
}
Why is it that it says Sedan does not contain the definition of all these properties when it should just simply inherit from the base class?
SpeedUp
不是 Speed
。我认为您没有复制所有代码,或者您需要重建项目。
Why do I need a setter now?
因为 get
-only auto-属性 只能在封闭 class.[=23 的构造函数 中赋值=]
Just like a readonly field, a getter-only auto-property can also be assigned to in the body of a constructor of the enclosing class. Such an assignment assigns directly to the readonly backing field of the property.
所以基本上你是在分配给 属性 的支持字段,即 readonly
。如果你读到 behaviour of readonly
,这个限制的原因就更清楚了:
Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class.
Sedan
不是 LicensePlate
、Speed
和 Wheels
等的封闭 class,因此不能在 [=13 中设置它们=]的构造函数。
Get-only 属性只能在声明它们的 class 中直接初始化,在构造函数中或在定义 属性 成员的地方直接赋值它们。
C# 语言规范的相关部分解决了这个问题。 C# 规范声明
When a property is specified as an automatically implemented property, a hidden backing field is automatically available for the property, and the accessors are implemented to read from and write to that backing field. If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). Just like a readonly field, a getter-only auto-property can also be assigned to in the body of a constructor of the enclosing class. Such an assignment assigns directly to the readonly backing field of the property.
它还指出
When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class. (A readonly field can be assigned to multiple times in these contexts.) Specifically, direct assignments to a readonly field are permitted only in the following contexts:
- In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
- For an instance field, in the instance constructors of the class that contains the field declaration; for a static field, in the static constructor of the class that contains the field declaration. These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.
那么你有什么选择?
通过调用基础 class 构造函数将参数传递给基础 class,并在那里设置您的属性(可能是最好的方法)。
将 protected
添加到您的属性中,以便您的子class 可以设置它们。次优,因为现在所有 subclasses 都可以以无数种(可能不一致的)方式设置这些属性(例如,在构造 class 之后,改变状态)。
我目前正在学习 C#,并且在玩耍时观察到无法解释的行为。
鉴于此界面:
using System;
namespace LearnInterfaces
{
interface IAutomobile
{
string LicensePlate { get; }
double Speed { get; }
int Wheels { get; }
void Honk();
}
}
我这样实现 class :
using System;
namespace LearnInterfaces
{
class Sedan : IAutomobile
{
public Sedan(double speed, int wheels){
this.Speed = speed;
this.LicensePlate = Tools.GenerateLicensePlate();
this.Wheels = wheels;
}
public string LicensePlate
{ get; }
public double Speed
{ get; }
public int Wheels
{ get; }
publc void Honk(){.....};
}
}
这很好,我可以在我的主程序中实例化 Sedan。
但是,当涉及到继承+接口时,这种行为就不行了:
如果我决定添加一个新的基础 class 并使 Sedan
继承它:
class Vehicle
{
public string LicensePlate
{ get; }
public double Speed
{ get; }
public int Wheels
{ get; }
public void Honk()
{
Console.WriteLine("HONK!");
}
}
已更改,以便 Sedan 从中继承:
class Sedan : Vehicle, IAutomobile
{
public Sedan(double speed)
{
Speed = speed;
LicensePlate = Tools.GenerateLicensePlate();
Wheels = 4;
}
}
现在,如果我尝试从我的主程序实例化,我会得到 Property or indexer 'Vehicle.Speed' cannot be assigned to -- it is read only
。 class.
当我让 Sedan 从基础 class 继承时,为什么现在需要 setter?据我了解,Sedan 也将继承 getter;所以用构造函数实例化它应该没问题(就像前面的例子一样,它只是实现了接口)。
这取决于您的约束条件和您想要实现的目标。但是,问题是因为您的基础 class 只获取属性。这些属性只能在构造函数中设置,不包括派生构造函数。实现此目的的最简单(可能不是最佳)方法是在基础 class 上使用 protected setter,这使得这些属性对于派生的 classes 是可变的。另一种方法是在你的基础上有构造函数 class
A protected member is accessible within its class and by derived class instances.
public string LicensePlate { get; protected set; }
public double Speed { get; protected set; }
public int Wheels { get; protected set; }
Why do I need a setter now when I make
Sedan
inherit from a base class? In my understanding, theSedan
will also inherit the getter; So instantiate it with constructor should be ok (just like previous example where it simply implementes the interface).
在只有 get
的 C# 属性中,只能由它们自己的构造函数初始化,而不能由子 class 中的构造函数初始化。您需要向 Vehicle
添加一个构造函数,它接受 LicensePlate
、Speed
和 Wheels
的值,然后将这些值传递给父级 class'构造函数:
public class Vehicle
{
protected Vehicle( string licensePlate, double speed, int wheels )
{
this.LicensePlate = licensePlate;
this.Speed = speed;
this.Wheels = wheels;
}
// etc
}
public class Sedan : Vehicle, IAutomobile
{
public Vehicle( string licensePlate, double speed, int wheels )
: base( licensePlate, speed, wheels )
{
}
}
Why is it that it says Sedan does not contain the definition of all these properties when it should just simply inherit from the base class?
SpeedUp
不是 Speed
。我认为您没有复制所有代码,或者您需要重建项目。
Why do I need a setter now?
因为 Just like a readonly field, a getter-only auto-property can also be assigned to in the body of a constructor of the enclosing class. Such an assignment assigns directly to the readonly backing field of the property. 所以基本上你是在分配给 属性 的支持字段,即 Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class.get
-only auto-属性 只能在封闭 class.[=23 的构造函数 中赋值=]
readonly
。如果你读到 behaviour of readonly
,这个限制的原因就更清楚了:
Sedan
不是 LicensePlate
、Speed
和 Wheels
等的封闭 class,因此不能在 [=13 中设置它们=]的构造函数。
Get-only 属性只能在声明它们的 class 中直接初始化,在构造函数中或在定义 属性 成员的地方直接赋值它们。
C# 语言规范的相关部分解决了这个问题。 C# 规范声明
When a property is specified as an automatically implemented property, a hidden backing field is automatically available for the property, and the accessors are implemented to read from and write to that backing field. If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). Just like a readonly field, a getter-only auto-property can also be assigned to in the body of a constructor of the enclosing class. Such an assignment assigns directly to the readonly backing field of the property.
它还指出
When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class. (A readonly field can be assigned to multiple times in these contexts.) Specifically, direct assignments to a readonly field are permitted only in the following contexts:
- In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
- For an instance field, in the instance constructors of the class that contains the field declaration; for a static field, in the static constructor of the class that contains the field declaration. These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.
那么你有什么选择?
通过调用基础 class 构造函数将参数传递给基础 class,并在那里设置您的属性(可能是最好的方法)。
将
protected
添加到您的属性中,以便您的子class 可以设置它们。次优,因为现在所有 subclasses 都可以以无数种(可能不一致的)方式设置这些属性(例如,在构造 class 之后,改变状态)。