Base class object as argument for derived class

4.8k views Asked by At

(Simplified) Scenario:

public class BaseClass
{
    public int BaseClassInt {get; set;}

    public BaseClass(int pBaseClassInt)
    { this.BaseClassInt = pBaseClassInt; }

}

public class DerivedClass : BaseClass
{
    public int DerivedClassInt {get; set;}

    public DerivedClass (int pBaseClassInt, int pDerivedClassInt) : base(pBaseClassInt)
    { this.DerivedClassInt = pDerivedClassInt; }

}

If I want to instantiate a DerivedClass-object I have to pass all arguments required to create a BaseClass-object and a DerivedClass-object. Also for every BaseClass-constructor I have to (at least should in my concrete case) provide a constructor with the same arguments in the derived class, plus arguments for the derived class properties. Then, if I change or delete a constructor in the base class I have to change or delete the corresponding contructor in the derived class(es).

I wonder if it is possible to use a constructor for the derived class which accepts a base class-object as an argument:

public DerivedClass(BaseClass pBaseClassObejct, int pDerivedClassInt)
{
    // to make clear what I intend to do - looks silly of course
    this = (DerivedClass)pBaseClassObject;
    this.DerivedClassInt = pDerivedClassInt;
}

This could be called:

DerivedClass DerivedClassObject = new DerivedClass((new BaseClass(1),2);

If constructors in the base class would change, I wouldn´t have to mind it for the derived class. Is there any way to achieve this?

5

There are 5 answers

0
Kjartan On BEST ANSWER

Think about this line for a moment:

this = (DerivedClass) pBaseClassObject;

Let's ignore the fact that you cant set this directly that way, and focus on the rest.

Imagine Giraffe and Elephant are both implementations of AfricanAnimal:

// By extension, ellie is also an AfricanAnimal    
Elephant ellie = new Elephant(); 

// assume ellie is passed in as a param here (she can 
// be, because she is an AfricanAnimal after all!):
public Giraffe(AfricanAnimal ellie) 
{
    this = (Giraffe) ellie; // Can't do this!
}

You can't (and would not want to) force ellie into being a giraffe, because a giraffe may have properties etc. that ellie lacks, and ellie may have properties that Giraffes don't have. Yet, using an AfricanAnimal as your parameter type there, would allow for just that.

Note: You could write that code and pass a Giraffe in, and all would be fine, but then again, that makes little sense; then you might as well use the Giraffe type as the parameter.

If you replace this with an instance variable, you would be able to compile with something like the following...

public Giraffe(AfricanAnimal ellie) 
{
    this.varOfTypeGiraffe = (Giraffe) ellie; 
}

... but as soon as you run it with an Elephant as a a prameter, you will get an exception similar to:

InvalidCastException: Unable to cast object of type 'Elephant' to type 'Giraffe'.

0
Jeppe Stig Nielsen On

This feature is not available. I think what you want is a little like this:

Suppose C# had a keyword allbaseargs and allowed code like this:

public class DerivedClass : BaseClass
{
  public int DerivedClassInt { get; set; }

  public DerivedClass (allbaseargs, int pDerivedClassInt)
    : base(allbaseargs)
  {
    DerivedClassInt = pDerivedClassInt;
  }
}

Then this could only work if BaseClass had only one (accessible) instance constructor.

The compiler should then examine the sole base constructor and substitute the magical word allbaseargs with the parameters of that constructor.

However, C# does not have this feature, and you would have to hand-code everything, which includes changeing all : base(...) calls of all derived classes when the constructor signature changes.

It is allowed to have the signature:

public DerivedClass(BaseClass pBaseClassObejct, int DerivedClassInt)

like you suggest, but you would not be able to chain the : base(...) easily. You would have to equip BaseClass with a construtor that took another instance in and copied all "state" (all instance properties and fields and such) from that other instance to "this". I do not recommend that solution.

2
Jon On

TL;DR: This is a bad idea. Don't even try.

You cannot make a base constructor run from inside the body of any derived method (including the derived constructor). Even if you could, a base instance would not have retained any information about which constructor was used to instantiate it so there would be no way to know which base constructor should be called.

The above refers to the general case where a base constructor can potentially modify application state not directly related to the base class (e.g. by changing the value of static fields somewhere). You could use reflection to copy property values from a base instance to the derived instance being created, but this is practically unworkable because

  1. It requires that you create a base instance in the first place -- what if the base is abstract, or if creating one has side effects?
  2. You need a guarantee that the base constructor does not modify application state. But the aim here is to be independent of what the base constructors do, so you are back to square one.
0
Mert Akcakaya On

No, that is not possible and should not be, because it doesn't make sense.

If it was possible and you deleted/changed the base class constructor, you would still need to change the code which creates the base class object that you would use as an argument to the derived class constructor.

Also, not all base classes are concrete. You would not be able to create an abstract base class, right?

0
Mohammadreza Askari On

This might be help!

Solution A: Create Inherit instead of base!

public static class Test
{
    public static T Foo<T>(string text, int num) where T : BaseClass
    {
        T @base = (T)Activator.CreateInstance(typeof(T), new object[] { text, num });
        //...
        return @base;
    }

    public static void Main()
    {
        InheritClass inherit = Foo<InheritClass>("Hi there", 10);
    }
}

Solution B: Copy base to inherit

public static class Test
{
    public static TInherit As<TBase, TInherit>(this TBase @this) where TInherit : TBase
    {
        var type = typeof(TInherit);
        var instance = Activator.CreateInstance(type);
        foreach (var property in type.GetProperties())
            if (property.CanWrite)
                property.SetValue(instance, property.GetValue(@this, null), null);
        return (TInherit)instance;
    }
    public static BaseClass Foo(string text, int num)
    {
        BaseClass @base = new BaseClass(text, num);
        //...
        return @base;
    }

    public static void Main()
    {
        InheritClass inherit = Foo("Hi there", 10).As<BaseClass, InheritClass>();
    }
}

Notes: you can have simple 'As()' found here, but i prefer mine (where Inherit : TBase), where it's more safe and support converting base to inherit of inherit class.