9

I am trying to implement good design patterns for a program I am writing. I have a class structure like this.

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))
     {

     }

}

Now as I'm sure you know, you can't pass this in a base constructor, because the object hasn't been initialized at this point in time. Anyhow I was really hoping there was a workaround. It's not best practice for me to allow SomeDerived() to handle the setting of a base classes field. I would like to pass this new object up the chain.

d219
  • 2,707
  • 5
  • 31
  • 36
guitar80
  • 716
  • 6
  • 19
  • 3
    Would other subclasses want to create an instance of `SomeObject` which *doesn't* refer to `this`? If not, just put that logic into the `SomeBase` constructor body: `obj = new SomeObject(this);` – Jon Skeet Sep 18 '14 at 17:36
  • Why do you want this? I don't see the need. – Patrick Hofman Sep 18 '14 at 17:36
  • Well, I do think what I am doing is useful. So, I have a StateMachine working within a game. There is a StateManager class that holds 3 various types of States (Action, Size, Vulnerability). States need to have the ability to new up other states and replace themselves (a requirement by my professor). – guitar80 Sep 18 '14 at 17:39
  • So... States require a reference to the GameObject they alter. StateManagers hold this reference as well so they can pass it to the appropriate States they initialize. The issue is in the GameObject constructor passing up a "GameObject2D(...) : base(new StackManager(this))" – guitar80 Sep 18 '14 at 17:41
  • I have a workaround but it's not ideal as each derived class of GameObject2D would need to initialize their StateManager. And in answer to you, Jon, only things with a State would need to. Most of which are GameObjects, the only exception might be if the Game or Menu had state logic (possibly). – guitar80 Sep 18 '14 at 17:43
  • @PatrickHofman for example the derived class implements an interface that the base class constructor needs an instance of. – rory.ap Dec 18 '19 at 12:53

3 Answers3

5

This is not possible, use an Init method after the constructor:

 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));
      }

 }
Peter Krassoi
  • 571
  • 3
  • 11
1

Well, you actually have it in the base constructor, so there is no need to pass it.

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

Another solution will be to use the lambda expression, so the SomeDerived class will have total control on how the reference to it will be passed into 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

Konard
  • 2,298
  • 28
  • 21
  • 1
    Why do you use reflection to create `SomeObject`? Surely you can just write `obj = new SomeObject(this)`? – ProgrammingLlama Aug 04 '20 at 08:24
  • Otherwise it will not compile. You can try it youself, you'll get `error CS1502: The best overloaded method match for 'SomeObject.SomeObject(SomeDerived)' has some invalid arguments` and `error CS1503: Argument '#1' cannot convert 'SomeBase' expression to type 'SomeDerived'`. `Activator` here is a way to choose the correct constructor at runtime. This way you will be able to pass the reference to `SomeDerived` class from the `SomeBase` constructor. Here is [a demo](https://repl.it/@Konard/PartialSpiffyCylinder), I`ve created for you. – Konard Aug 04 '20 at 16:35
  • But you can use `obj = new SomeObject((SomeDerived)this);` here, if you don`t like reflection. I've updated the answer. But this creates a dependency up on the derived class right it the base class, so I'll decided to leave two options. – Konard Aug 04 '20 at 16:51
  • But SomeObject wasn't defined in the question so I have no reason to believe that the constructor requires a SomeDerived. – ProgrammingLlama Aug 04 '20 at 23:25
  • @John SomeObject is pretty much defined in the question, we know exactly that it has a constructor that receives SomeDerived class instance. In the question it is written `I would like to pass this new object up the chain.`. In my answer I just wanted to make a point that there is no need to pass SomeDerived object up to base class, because the reference is already there. – Konard Aug 05 '20 at 08:13
  • _"we know exactly that it has a constructor that receives SomeDerived class instance"_ - and `SomeDerived` is derived from `SomeBase`, so the constructor parameter could expect a `SomeBase`, which is fulfilled by its derivative. I guess it doesn't matter on this old question. – ProgrammingLlama Aug 05 '20 at 08:20
  • 1
    @John check out my new more elegant solution with lambda function, this way the base class does not need to know anything about the derived class, and there is no reflection at all now. This second solution does exactly what the question asks. And the control over SomeObject creation stays withing SomeDerived scope. – Konard Aug 05 '20 at 08:29
-10

1) Constructors are WRONG things by it's design at all - it's looks like method of instance, but really it's half-method of half-instance.

2) "Good design programs with patterns" do not cause immediate circular dependency between classes in aggregation as we see here - both classes must know and use each other at creation(!!!) who knows what SomeObject do with "this" in it's constructor???

So in "patterns" where are 2 problems - high dependency between classes and not-usable incapsulation of initialization logic. So we have to find "pattern" way to solve it... hmm.. what to do...

In code u provide i see that derived class just provides it's own logic for property obj u can rewrite it to be auto-initialized propery

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());
    }   
}

For your example such solution provides single "pattern" that win - "polymorphism" )) and gain additional bonus - if "Obj" will not be usefull - it never be created ))))

comdiv
  • 865
  • 7
  • 26
  • Maybe I am misunderstanding, but I don't think this helps for my problem. If you read the comments below my original post, I describe the problem in more detail. Sorry if this is just my confusion though. – guitar80 Sep 18 '14 at 17:57
  • No. I think U misunderstood your problem. U try to make something "patterned" but not understand basic problems (minimal surface area between object, minimization of dependency, factoring of classes, modularity) that are laying under any pattern. So you make your code not usefull and comfortable even for yourself! CONSTRUCTOR - is your problem!!! you try express not-trivial construction logic with CONSTRUCTORS - IT'S NOT PATTERN. If U have not trivial construction U must use patterns of Factory family, instead of braincrashing with overriding constructors – comdiv Sep 18 '14 at 18:12