JayByte JayByte - 2 months ago 8
C# Question

Downcast an object, that is declared in a base class, in the inherited class

I'm creating a plug-in for an API and have problems with inheritance. Is there a way to "automatically" downcast an object, that is declared in a base class, in a class inherited from it?

My class structure is as follows:

abstract class MyPart
{
public Part modelObject;
...
}

class MyPlate : MyPart
{
public ArraList points; // from API
...
}

class Floor : MyPlate
{
...
}


Part
is an API class whose class structure is something like the following:

class Part
class Plate : Part


Now, in my code in
MyPlate
and
Floor
classes I need to use
Plate modelObject
to get access to it's methods. Currently I do this by downcasting
modelObject
:

(modelObject as Plate).doSomething;


This kinda works, though I would like to make it simpler. The main problem, however, is that when I try to access the members of
modelObject
I can't get them as references. For example when I try to declare
points
it doesn't reference
modelObject.points
:

points = (modelObject as Plate).points;
points = null; // modelObject.points != null


Is there a way to declare variables as references to
modelObject
so that they could be manipulated?

Answer

These are actually two questions. The part, why your points won't get deleted has already been answered. But for completeness, here the summary:

Deleting Refrences

When you call var points = (modelObject as Plate).points, you copy a reference to the points object of modelObject. With points = null you just remove (null) the reference to the points of modelObject, but modelObject still keeps a reference to points.

To delete modelObject's reference to points, call (modelObject as Plate).points = null.

Downcasting in Sub-Class

But to your main question, how to automatically downcast an object in the subclass. There are two ways to achieve this. Method Hiding and Generics:

Method Hiding

Methhides a method or another member of the baseclass, and provides a new meaning to a name. That way, you can give the subclass-member another meaning and another return type. To achieve this, you should encapsulate modelObject in a Property. Your code would like this:

abstract class MyPart
{
    private Part modelObject;
    public Part ModelObject 
    {
       get { return modelObject; }
       set { modelObject = value; }
    }
    ...
}

class MyPlate : MyPart
{
    public new Plate ModelObject 
    {
        get { return base.ModelObject as Plate; }
        set { base.ModelObject = value; }
    public ArraList points; // from API
    ...
}

class Floor : MyPlate
{
    ...
}

Notice, the use of base.ModelObject in the subclass. This enables you to still access the base-classes ModelObject, although you have given a new meaning to ModelObject. Other than method overriding, you keep the base classes Member and only provide another meaning with the same name.

Watch out, this has some implications. The most important one is, that a class does not know, if a member is hidden in a subclass, which means, that a subclass, casted to the base class, will always run the base-class-functionality. See the following code-sample for illustration purposes.

public class Base 
{
    public void Do() 
    {
        Console.WriteLine("Base");
    }
}
public class Sub : Base
{
    public new void Do() 
    {
        Console.WriteLine("Sub");
    }
}
public class Program 
{
    public static void Main(string[] args) 
    {
        Sub sub = new Sub();
        Base base = sub;

        sub.Do(); //prints Sub
        base.Do(); //prints Base, even though it is the same object.
    }
}

Generics

With generics on the other hand, you can parametrize the type of Plate with a type-parameter (TPlate in the example). This is slightly more complicated, but also the cleaner way, since subclassing and casting has no confusing effects like method hiding.

abstract class MyPart<TPart> 
    where TPart : Part //configure a type parameter
{
    public TPart modelObject;
    ...
}

class MyPlate : MyPart<Plate> //TPlate is now of type Plate
{
    public ArraList points; // from API
    ...
}

class Floor : MyPlate
{
    ...
}

The above example doesn't allow MyFloor to further specialize the Type Parameter, but this can also be achieved:

abstract class MyPart<TPart> 
    where TPart : Part //configure a type parameter and force it to be a subclass of Part
{
    public TPart modelObject;
    ...
}

class MyPlate<TPlate> : MyPart<TPlate> 
    where TPlate : Plate //force the type parameter to be a subclass of Plate
{
    public ArraList points; // from API
    ...
}

class Floor : MyPlate<Floor> //TType is now Floor
{
    ...
}
Comments