【问题标题】:Pass this to base constructor将此传递给基本构造函数
【发布时间】:2014-09-18 17:34:31
【问题描述】:

我正在尝试为我正在编写的程序实现良好的设计模式。我有这样的类结构。

abstract class SomeBase
{
     public SomeObject obj { get; protected set; } 

     protected SomeBase(SomeObject x)
     {
           obj = x;
     }

     //Other methods and fields...
}

public class SomeDerived : SomeBase
{

     public SomeDerived() : base(new SomeObject(this))
     {

     }

}

现在我确定你知道,你不能在基础构造函数中传递它,因为此时对象还没有被初始化。无论如何,我真的希望有一个解决方法。让SomeDerived() 处理基类字段的设置对我来说不是最佳做法。我想将这个新对象向上传递。

【问题讨论】:

  • 其他子类是否想创建一个引用thisSomeObject实例?如果没有,只需将该逻辑放入 SomeBase 构造函数主体中:obj = new SomeObject(this);
  • 你为什么要这个?我看不出有必要。
  • 嗯,我确实认为我所做的很有用。所以,我有一个在游戏中工作的 StateMachine。有一个包含 3 种不同类型的状态(操作、大小、漏洞)的 StateManager 类。州需要有能力更新其他州并替换自己(我的教授的要求)。
  • 所以...状态需要引用它们改变的游戏对象。 StateManagers 也持有这个引用,因此他们可以将它传递给他们初始化的适当状态。问题在于 GameObject 构造函数传递了“GameObject2D(...) : base(new StackManager(this))”
  • 我有一个解决方法,但这并不理想,因为 GameObject2D 的每个派生类都需要初始化它们的 StateManager。乔恩,作为对你的回答,只有与国家有关的事情才需要。其中大部分是游戏对象,唯一的例外可能是游戏或菜单是否具有状态逻辑(可能)。

标签: c# constructor abstract-class derived-class base-class


【解决方案1】:

这是不可能的,在构造函数之后使用Init方法:

 abstract class SomeBase
 {
      private SomeObject _obj { get; set; } 
      public SomeObject obj 
      {
           get 
           {    // check _obj is inited:
                if (_obj == null) throw new <exception of your choice> ;
                return _obj;
           } 
      }

      protected SomeBase()
      {
           obj = null;
      }

      protected void Init()
      {
           obj = x;
      }

      //Other methods and fields...
 }

 public class SomeDerived : SomeBase
 {

      public SomeDerived() : base()
      {
           Init(new SomeObject(this));
      }

 }

【讨论】:

  • 谢谢。这基本上就是我所做的。我想我希望有一些魔法。哈哈谢谢简洁的回答
  • 或者你可以创建一个抽象属性。较小的代码。不知道有没有缺点。
【解决方案2】:

嗯,其实你在基础构造函数中就有了,所以不需要传递它。

using System;

abstract class SomeBase
{
    public SomeObject obj { get; protected set; }

    protected SomeBase()
    {
        // Will be executed as 1st step
        Console.WriteLine(1);

        // "this" here is SomeDerived object
        obj = new SomeObject((SomeDerived)this);
        // If you don`t like to depend upon SomeDerived type here,
        // you can do the same using the following line:
        //obj = (SomeObject)Activator.CreateInstance(typeof(SomeObject), this);
    }
}

class SomeObject
{
    public SomeObject(SomeDerived obj)
    {
        if (obj.obj == null)
        {
            // Will be executed as 2nd step
            Console.WriteLine(2);

            // You have the reference to SomeDerived here
            // But its properties are not yet initialized
            // (both SomeDerived and SomeBase constructors are in the progress)
            // So you should not access these properties 
            // in the SomeObject class constructor,
            // but you can do it in any method of SomeObject class 
            // (at the time the method will be called all constructors are finished).
        }
    }
}

class SomeDerived : SomeBase
{
    public SomeDerived()
    {
        // Will be executed as 3rd step
        Console.WriteLine(3);

        if (this.obj != null)
        {
            // You have the reference to SomeObject here,
            // which itself already got an access to SomeDerived reference
        }
    }
}

class MainClass 
{
    public static void Main (string[] args) 
    {
        var instance = new SomeDerived();
        Console.WriteLine (instance.obj); // Outputs SomeObject
    }
}

https://repl.it/@Konard/LinenZealousBlockchain

另一种解决方案是使用 lambda 表达式,因此 SomeDerived 类将完全控制对它的引用如何传递给 SomeObject。

using System;

abstract class SomeBase
{
    public SomeObject obj { get; protected set; }

    protected SomeBase(Func<SomeBase, SomeObject> someObjectInitilizer)
    {
        // Will be executed as 1st step
        Console.WriteLine(1);

        // "this" here is SomeDerived object
        obj = someObjectInitilizer(this);
    }
}

class SomeObject
{
    public SomeObject(SomeDerived obj)
    {
        if (obj.obj == null)
        {
            // Will be executed as 2nd step
            Console.WriteLine(2);

            // You have the reference to SomeDerived here
            // But its properties are not yet initialized
            // (both SomeDerived and SomeBase constructors are in the progress)
            // So you should not access these properties 
            // in the SomeObject class constructor,
            // but you can do it in any method of SomeObject class 
            // (at the time the method will be called all constructors are finished).
        }
    }
}

class SomeDerived : SomeBase
{
    public SomeDerived() : base((@this) => new SomeObject((SomeDerived)@this))
    {
        // Will be executed as 3rd step
        Console.WriteLine(3);

        if (this.obj != null)
        {
            // You have the reference to SomeObject here,
            // which itself already got an access to SomeDerived reference
        }
    }
}

class MainClass 
{
    public static void Main (string[] args) 
    {
        var instance = new SomeDerived();
        Console.WriteLine (instance.obj); // Outputs SomeObject
    }
}

https://repl.it/@Konard/ParchedYellowgreenType

【讨论】:

  • 为什么要用反射来创建SomeObject?当然你可以写obj = new SomeObject(this)
  • 否则无法编译。你可以自己试试,你会得到error CS1502: The best overloaded method match for 'SomeObject.SomeObject(SomeDerived)' has some invalid argumentserror CS1503: Argument '#1' cannot convert 'SomeBase' expression to type 'SomeDerived'Activator 这是一种在运行时选择正确构造函数的方法。这样,您将能够从 SomeBase 构造函数传递对 SomeDerived 类的引用。这是a demo,我为你创建的。
  • 但是如果你不喜欢反射,你可以在这里使用obj = new SomeObject((SomeDerived)this);。我已经更新了答案。但这会在派生类上产生对基类的依赖,因此我决定保留两个选项。
  • 但是 SomeObject 没有在问题中定义,所以我没有理由相信构造函数需要 SomeDerived。
  • @John 使用 lambda 函数查看我的新的更优雅的解决方案,这样基类不需要了解派生类的任何信息,并且现在根本没有反射。第二个解决方案正是问题所要求的。并且对 SomeObject 创建的控制保持在 SomeDerived 范围内。
【解决方案3】:

1) 构造函数的设计完全是错误的——它看起来像实例方法,但实际上它是半实例方法的半方法。

2)“具有模式的良好设计程序”不会导致聚合中的类之间立即出现循环依赖,正如我们在此处看到的那样 - 两个类必须在创建时相互了解和使用(!!!)谁知道 SomeObject 对“this " 在它的构造函数中???

所以在“模式”中存在两个问题 - 类之间的高度依赖和初始化逻辑的不可用封装。所以我们必须找到“模式”的方式来解决它……嗯……怎么办……

在你提供的代码中,我看到派生类只是为属性 obj 提供了它自己的逻辑 您可以将其重写为自动初始化属性

public abstract class MyClass{
     private SomeObject _obj ; 
     public SomeObject Obj {get { return  _obj ?? (_obj = InitializeObj() );}} //no setter needed
     protected abstract SomeObject InitializeObj();
}

public class MyRealClass:MyClass {
    protected override SomeObject InitializeObj(){
        return new VerySpecialSomeObject(this, other, another, 1, 2 , false, option: new Options());
    }   
}

对于您的示例,此类解决方案提供了获胜的单一“模式”-“多态性”))并获得额外的奖励-如果“Obj”没有用-它永远不会被创建))))

【讨论】:

  • 也许我理解错了,但我认为这对我的问题没有帮助。如果您阅读我原始帖子下方的 cmets,我会更详细地描述该问题。抱歉,这只是我的困惑。
  • 没有。我想你误解了你的问题。你试图做一些“模式化”的东西,但不理解任何模式下的基本问题(对象之间的最小表面积、最小化依赖性、类的分解、模块化)。所以你让你的代码即使对你自己也没有用处和舒适! CONSTRUCTOR - 是你的问题!!!你尝试用 CONSTRUCTORS 表达不平凡的构造逻辑——这不是模式。如果 U 没有简单的构造,则必须使用 Factory 族的模式,而不是使用重写构造函数来脑残
猜你喜欢
  • 2012-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 2019-01-06
  • 1970-01-01
  • 2014-12-25
相关资源
最近更新 更多