【问题标题】:How can my base constructor access the derived instance?我的基本构造函数如何访问派生实例?
【发布时间】:2019-10-15 22:10:44
【问题描述】:

我有一个基类Animal 和一个派生类Lion。每只动物都应该提到丛林之王,它总是一头狮子。因此在基类中添加了一个只读属性Lion : KingOfTheJungle。该属性在Animal的构造函数中初始化。

这个想法是首先创建Lion 对象,然后将它的引用传递给所有其他动物的构造函数。丛林只有一只狮子,所以当狮子被建造时,应该承认自己是丛林之王。问题是我无法创建Lion,因为当我将this 作为参数传递给base() 时出现编译时错误:

abstract class Animal
{
    public Lion KingOfTheJungle { get; }

    public Animal(Lion theKing)
    {
        KingOfTheJungle = theKing;
    }
}

class Lion : Animal
{
    public Lion() : base(this) // Error CS0027
    {
    }
}

我得到的错误是:

错误 CS0027 关键字“this”在当前上下文中不可用

寻找解决方法我最终在Lion 的构造函数中传递了null 而不是this,并在Animal 的构造函数中将null 插入为this

abstract class Animal
{
    public Lion KingOfTheJungle { get; }

    public Animal(Lion theKing)
    {
        KingOfTheJungle = theKing ?? (Lion)this; // OK, but unsafe
    }
}

class Lion : Animal
{
    public Lion() : base(null) // OK, but not expressive
    {
    }
}

不过,我不喜欢这种解决方法,因为它既不具表现力,也不安全。有没有更安全或至少更优雅的方式来做同样的事情?

注意:我想保持属性 KingOfTheJungle 只读,没有 protected 设置器。动物在初始化后应该不能改变这个属性。

【问题讨论】:

  • @mjwills 我试过了,这是一个可行的解决方案。不过我并不完全热衷于它,因为它为每个派生类增加了两个职责。 1)添加行public override Lion KingOfTheJungle { get; }和2)在其构造函数中分配属性KingOfTheJungle。 (2) 让我担心我可能会忘记它,因为它不是由编译器强制执行的,而且我有一堆派生类要实现。
  • 恕我直言,这个要求需要提升一个级别。换句话说,如果丛林有一个硬性要求,即只有一头狮子是国王,并且没有其他动物可以在不知道它的国王的情况下存在,那么丛林的存在需要狮子作为构造的参数,或者存在这样的约束:没有国王,任何动物都不能加入丛林。了解其子类的基类是设计灾难;没有解决办法。
  • @ChiefTwoPencils 我知道一个用于构建健壮的 OOP 应用程序的框架,其中基类知道它的后代。后代的类型作为类型参数传递,如下所示:public class Customer : BusinessBase<Customer> (source)。它是 Rockford Lhotka 的 CSLA .NET 框架。您的批评是否也适用于此?
  • 但是该示例不知道后代,因为它是通用的。这与 明确 依赖于 actual 类型不同。
  • Animal 毫无疑问地知道存在后代时(总是);这是一个循环依赖。没有它的父母(动物),后代(狮子)就不能存在,没有国王(又名狮子),动物就不能存在;这不是设计事物的好方法。\

标签: c# inheritance constructor compiler-errors


【解决方案1】:

我建议KingOfTheJungle 可以是基类中的virtual(或abstract)只读属性。然后在继承类中覆盖它。

类似的东西:

abstract class Animal
{
    public abstract Lion KingOfTheJungle { get; }    
}

class Lion : Animal
{
    public override Lion KingOfTheJungle
    {
        get
        {
            return this;
        }
    }
}

【讨论】:

  • 我用这个解决方案替换了我的解决方法,因为它显然更强大。最终,尽管我可能应该听从 Peter Duniho 的建议,并通过引入 Jungle 类来改变我的设计。 Jungle 对象将持有对 King Lion 的引用,并且所有 Animals 将持有对 Jungle 的引用。这是一个更合理的设计,作为奖励,它会让我当前的问题消失。
【解决方案2】:

正确的做法是使用static 属性:

public static Lion KingOfTheJungle { get; } = new Lion();

这解决了几个问题:

  • 国王的非确定性加冕。采用这种方法,总有一个国王。
  • 国王太多。使用这种方法,总是有 one Lion 是王者。任何其他 Lion 实例都只是普通的 Lion
  • 不同的Animal 实例认为不同的Lion 实例是他们的王,这取决于在什么时间实例化了哪个Lion
  • 同一参考文献的多个副本。
  • 无法将this 传递给基本构造函数。现在不需要了。

如果您真的想追求您的原始设计,恕我直言,请记住,构造函数已经拥有this 参考。请记住,this 是同一个对象,无论代码存在于继承层次结构中的哪个类。

这意味着一种“更好”的方式来做到这一点,但仍然遵循你最初的想法,看起来像这样:

abstract class Animal
{
    public Lion KingOfTheJungle { get; }

    public Animal()
    {
        KingOfTheJungle = (this as Lion) ?? KingOfTheJungle;
    }
}

这仍然存在我上面提到的最后一个问题。我不会以这种方式实现它。

你提到丛林只有一个Lion。这进一步表明您应该将 Lion 类设为单例。看起来像这样:

abstract class Animal
{
    public static Lion KingOfTheJungle { get; } = Lion.Instance

    public Animal()
    {
        // ...
    }
}

class Lion : Animal
{
    public static Lion Instance { get; } = new Lion();

    private Lion() { }
}

【讨论】:

  • 感谢彼得的回答。我考虑了static Lion KingOfTheJungle 的想法,但就我而言,我可以拥有多个丛林,每个丛林都有自己的国王。所以不幸的是,这个解决方案不能在我的情况下使用。
  • 实际上我的设计中没有 Jungle 类(顺便说一句,这与枚举和分类器而不是动物有关)。丛林由拥有丛林属性的国王定义。现在想想这不是一个伟大的设计!
  • “我的设计中没有 Jungle 类”——这听起来像是个问题。您的类型层次结构应该合理地为您的实际问题域建模。如果您有丛林的概念,并且可能有不止一个这样的丛林,但您的类型层次并没有反映这一点,您可能需要重新考虑这一点。否则,我上面列出的问题会妨碍您将每只动物都留在正确的丛林中。
猜你喜欢
  • 1970-01-01
  • 2011-02-24
  • 1970-01-01
  • 2018-07-21
  • 2021-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-05
相关资源
最近更新 更多