在C#中禁止使用settable属性覆盖get-only属性。
public class Example : IExample //// OR ExampleBase
{
public int Property { get; set; } //// This causes error.
}
public abstract class ExampleBase
{
public abstract int Property { get; }
}
我已经检查了question 1和question 2。
我知道如何避免该错误,,但我不知道为什么应该禁止这样做。
请告诉我为什么用settable属性覆盖get-only属性是错误的。
答案 0 :(得分:3)
TL; DR;
在C#中禁止使用settable属性覆盖或实现get-only属性。
部分正确。用settable属性实现仅获取属性是完全有效的-但是用settable属性覆盖仅获取属性是无效的。
长版本:
当我尝试编译您的代码时,我遇到了两个编译错误(在VS 2017中,这很重要)
错误CS0106修饰符“抽象”对此项目无效
错误CS0106修饰符“公共”对此项目无效
从接口的属性中删除public abstract
时,代码可以正常编译(删除了与之无关的抽象类):
public class Example : IExample
{
public int Property { get; set; }
}
public interface IExample
{
int Property { get; }
}
但是,当尝试使用抽象类并使用get / set属性覆盖getonly属性时,出现此编译错误:
错误CS0546'Example.Property.set':无法覆盖,因为'ExampleBase.Property'没有可覆盖的集访问器
对于以下代码(已删除接口,对于私有集,则出现相同的错误):
public class Example : ExampleBase
{
public override int Property { get; set; }
}
public abstract class ExampleBase
{
public abstract int Property { get; }
}
这实际上是显示在c#中重写和实现之间区别的好方法:
接口是合同。。它强制实现类型将其成员包括为公共API的一部分(不包括显式实现)。因此,当使用get-only属性实现接口时,可以向此属性添加setter,因为只要它具有getter,合同仍然会得到履行。
但是,基类不是合同。如果强制继承类具有完全相同的成员签名,但允许继承类覆盖虚拟成员(因此,相同的方法或属性将在这两个类中的实现方式有所不同)。实际上,派生类 是其基类的(特定)类型。
通常,如果要在基类中的set-only属性中添加setter,则必须使用关键字new
对其进行阴影处理,但这对抽象成员没有帮助-抽象成员必须在派生类中被覆盖-并且由于我们没有属性重载,因此您将必须添加一个方法来设置get-only属性的值,并明确实现它:
public class Example : ExampleBase
{
private int _property;
public override int Property { get { return _property; } }
public void SetProperty(int property)
{
_property = property;
}
}
public abstract class ExampleBase
{
public abstract int Property { get; }
}
为了完整起见-如果基本属性不是抽象的,您将使用new
关键字:
public class Example : ExampleBase
{
public new int Property { get; set; }
}
public class ExampleBase
{
public virtual int Property { get; }
}
答案 1 :(得分:1)
abstract
和public
修饰符在界面中不可用。
假设您的意思如下:
public class Example : ExampleBase
{
public override int Property { get; set; } //// This causes error.
//// public int Property { get; private set; } //// This causes error, too.
}
public interface IExample
{
int Property { get; }
}
public abstract class ExampleBase
{
public abstract int Property { get; }
}
实现接口(IExample
)时,可以添加一个setter。当扩展抽象类(ExampleBase
)时,您必须以必须实现抽象库指定的方式来实现该属性,即仅使用getter。
答案 2 :(得分:0)
我不能代替C#语言团队说话,但对我来说,这涉及到一致性和避免设计错误。
尽管CLR并不禁止使用它-您可以将一个属性视为一对GetProperty()
和SetProperty()
方法,可以在基类中定义一个,而在派生类中定义另一个class-绑定到属性中时,基本上就是在表达访问“资源”(通常是字段)的合同。
因此,当基类将属性声明为仅是getter时,您不希望具体的实现在同一位置公开setter。如果具体的类确实需要这样做,那么它可以通过定义一个单独的方法更好地传达其意图,因为这是一种“破坏”类合同的方法。
另一方面,当涉及到接口时,协定仅在“表面” API上:只需说必须实现该方法或getter。而且,您可能有一个接口定义了只读属性,而一个接口定义了仅设置属性(为什么不这样)。