简体   繁体   中英

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?

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
{
    ...
}

You are creating a copy of the reference . points = (modelObject as Plate).points; So points and (modelObject as Plate).points points to the same memory. When you assign null to points , one reference is cleared. The other still exists.

If you want the (modelObject as Plate).points; to be null. Just use: (modelObject as Plate).points = null;

This doesn't have anything to do with casting.

When you set points to null there you're nulling out the reference named "points" in the scope, not the actual variable. You want to manipulate it by ref to actually access that if you really don't want to just do modelObject.points = null (which you'd normally just do).

See https://msdn.microsoft.com/en-us/library/14akc2c7.aspx

That said, I'm not sure why you're downcasting the objects as we can't see enough of what's going on to determine that here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM