【问题标题】:C#: Semi-Abstract Automatic Properties?C#:半抽象自动属性?
【发布时间】:2011-08-12 02:46:13
【问题描述】:

在基类中,我想定义一个抽象的 get,但那时我并不关心集合。如何在我的子类中定义 setter?

我尝试了一些方法,但无法正常工作。例如我试过:

public class BaseClass
{
    public abstract bool MyBool { get; }
}

public class ChildClass : BaseClass
{
    public override bool MyBool { get; protected set;}
}

还有:

public class BaseClass
{
    public bool MyBool { abstract get; }
}

public class ChildClass : BaseClass
{
    public bool MyBool { override get; protected set;}
}

我知道我可以通过不在子类中使用自动属性并直接设置基础字段而不是创建 setter 来解决此问题,但我正在寻找更好的方法。

编辑:我不想在 BaseClass 中添加抽象设置器。

【问题讨论】:

  • 注意:目前,我在 BaseClass 中添加了 setter,并为所有不需要实现它的 ChildClass 抛出 NotImplementedException。
  • 自 2016 年以来一直在考虑此功能。有关其当前状态,请参阅 github.com/dotnet/csharplang/issues/1568

标签: c# oop properties abstract


【解决方案1】:

使用接口而不是基类可能更有意义。然后,您只需让需要提供该属性的类实现该接口。

例如,您可以创建这个接口:

public interface IBoolable {
     bool MyBool { get; }
}

那么像这样实现接口还是有效的:

public class BoolableItem : IBoolable {
     public bool MyBool { get; protected set; }
}

通过这种方式,您的代码可以安全地假定任何实现 IBoolable 的东西都有一个名为 MyBool 的属性,该属性至少是只读的。

【讨论】:

  • 我会用这个,但稍作修改。我仍然需要我的 BaseClass 类,但我会为此添加一个 IClass 接口,以及其他更多类似接口的东西。谢谢!
  • 啊,谢谢你,如果接口属性没有一个集合,我就无法实现一个集合。
【解决方案2】:

一种解决方案是使MyProperty 不是抽象的,而是将其实现委托给一个抽象的受保护的属性,孩子必须重写:

public abstract class BaseClass
{
    public bool MyBool { get { return MyBoolInternal; } }
    protected abstract bool MyBoolInternal { get; set; }
}

public class ChildClass : BaseClass
{
    protected override bool MyBoolInternal { get; set; }
}

【讨论】:

    【解决方案3】:

    你必须决定你想要什么行为:如果它被定义为抽象,那么派生类必须实现它。

    所以你应该这样做:

    public abstract class BaseClass
    {
        public abstract string MyProperty { get; set; }
    }
    
    
    public class DerivedClass : BaseClass
    {
        public override string MyProperty
        {
            get { return "myValue"; }
            set { /* do nothing, not applicable for this class */ }
        } 
    }
    

    不要抛出 NotImplementedException - 这不是您想要的,您只是希望 setter 对某些子类不执行任何操作。

    【讨论】:

    • 例外是为了确保它永远不会被调用。如果它曾经在我的子类机制中被调用过,那真的有问题!!!
    • 这对我来说看起来很糟糕。在很多情况下,我试图设置一个不存在的设置器,编译器非常有帮助地错误地告诉我它不存在。一个空的 setter 会看起来像是一个 setter,而实际上并不是。
    【解决方案4】:

    如果你想在继承的类中设置一个 setter 不明确会违反 OOP 原则——也就是说,如果一个类(抽象与否)有一个公共/受保护的 setter(抽象与否),那么所有继承类也必须;如果一个类没有,那么继承类一定不能。

    考虑这一点的另一种方法是考虑只读或读写是类契约的一部分的属性。由于继承类的实例必须遵守“Is-a 关系”(LSP),继承类不能在主类没有的地方“添加一个 setter”,因为主类有一个属性这一事实没有 as setter 是主类定义的一部分。实际上,由于主类无法通过 setter 更改相关属性,因此所有继承类必须保证相同的行为。

    考虑使用受保护的支持字段;然后您可以将此属性拆分为只读属性和单独的 setter 方法。然后,主类可以只有属性,子类可以有一个主类不知道的setter 方法。但是,我也不确定这是否是一个好的设计。

    【讨论】:

    • 我仍然不相信这个论点。如果您添加了一个 setter,任何依赖于 getter 的代码仍然可以工作。作为合同的一部分,它只是没有意义。考虑一下通过明确的 setter 和 getter 方法(就像在 Java 和 C++ 中必须有的那样)以及在派生类中添加 setter 方法是多么容易,不会被视为违反合同。跨度>
    • 在 .NET 中仍然可以使用显式的 setter/getter;将属性添加到语言中,并将属性的“只读”作为合同的一部分,而不仅仅是“省略的方法”,这是一个语言设计决定。 VB.NET 使用其 ReadOnly 关键字使这一点更加明确,但 C# 中也有相同的功能。
    • 我可以理解这是 VB 人员的一些武断决定,但我仍然不同意它违反了基本原则。您正在添加功能并且您没有更改现有功能或以其他方式违反 Liskov 替换原则。你可以这样想:基类说“我最多知道如何为这个属性提供值”。然后派生类出现并说“我可以提供这个值,但我也知道改变它的方法”。这就像一个具有 Area() 函数和派生类的形状类的经典案例......
    • ...有办法实际设置形状的参数,以便计算面积(例如具有长度和宽度,或周长)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-16
    • 2018-10-17
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    • 2014-07-12
    • 2011-11-29
    相关资源
    最近更新 更多