【问题标题】:Why not override get-only property with settable property?为什么不使用可设置属性覆盖 get-only 属性?
【发布时间】:2018-12-04 08:22:14
【问题描述】:

在 C# 中禁止使用可设置属性覆盖 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

我知道如何避免错误,但我不知道为什么要禁止。

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

【问题讨论】:

  • 您似乎正在将“公共摘要”添加到接口中,但这是行不通的。在您的 Example 类中,您没有覆盖任何内容(没有 override 关键字)。另请注意,{ get; } 不等同于{ get; private set; },它将等同于{ get; readonly set; }readonly set 不存在,但它是为了证明它与字段的readonly 关键字等效)。跨度>
  • 如果您使接口实际可编译,则接口版本应该可以工作。 (其实我只是在界面中不public abstract试了一下,确实可以编译)

标签: c# .net


【解决方案1】:

TL;DR;

在 C# 中禁止使用可设置属性覆盖或实现 get-only 属性。

部分正确。使用可设置属性实现仅获取属性是完全有效的 - 但使用可设置属性覆盖仅获取属性是无效的。

长版:

当我尝试编译您的代码时,我遇到了两个编译错误(在 VS 2017 中,如果这很重要的话)

错误 CS0106 修饰符“抽象”对此项无效
错误 CS0106 修饰符“public”对此项目无效

当从接口的属性中删除public abstract时,代码编译得很好(删除了抽象类,因为它在这里无关紧要):

public class Example : IExample
{
    public int Property { get; set; } 
}

public interface IExample
{
    int Property { get; }
}

但是,当尝试使用抽象类并使用 get/set 属性覆盖 get only 属性时,出现以下编译错误:

错误 CS0546“Example.Property.set”:无法覆盖,因为“ExampleBase.Property”没有可覆盖的 set 访问器

对于以下代码(删除了接口,私有集同样的错误):

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,契约仍然是被履行的。

但是,基类不是契约。如果强制继承类具有完全相同的成员签名,但允许继承类覆盖虚拟成员(因此相同的方法或属性将在两个类中实现不同)。事实上,派生类它的基类的(特定)类型。

通常,如果您想在基类中为 get-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; }
}

【讨论】:

  • @Zhohar 是的,我可以实现 IExample。我已经编辑了这个问题。我很抱歉造成混乱。现在我明白我不应该使用合同之类的基类。谢谢!
  • 很高兴为您提供帮助!请注意,我认为在编辑问题的过程中,您使答案无效。
【解决方案2】:

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。

【讨论】:

  • 是的,没错。我已经编辑了这个问题。很抱歉造成混乱。
【解决方案3】:

我不能代替 C# 语言团队发言,但对我来说,我认为这涉及到一致性和避免设计错误。

虽然 CLR 不禁止它 - 您可以将属性视为一对 GetProperty()SetProperty() 方法,其中一个可以在基类中定义,另一个在派生类中定义 - 捆绑时进入一个属性,你基本上是在表达一个访问“资源”(通常是一个字段)的合同。

因此,当基类将属性声明为仅是 getter 时,您不会期望具体实现会在同一个地方公开 setter。如果具体类确实需要这样做,那么它可以通过定义一个单独的方法更好地传达其意图,因为它有点“破坏”类契约。

另一方面,当涉及到接口时,合约只是在“表面”API 上:只是说必须实现该方法或该 getter。并且您可以有一个接口定义 get-only 属性和一个接口定义 set-only 属性(为什么不)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-24
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多