为什么不使用settable属性覆盖get-only属性?

时间:2018-12-04 08:22:15

标签: c# .net

在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 1question 2

我知道如何避免该错误,,但我不知道为什么应该禁止这样做。

请告诉我为什么用settable属性覆盖get-only属性是错误的。

3 个答案:

答案 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)

abstractpublic修饰符在界面中不可用。

假设您的意思如下:

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。而且,您可能有一个接口定义了只读属性,而一个接口定义了仅设置属性(为什么不这样)。