Reputation: 74
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?
Upvotes: 0
Views: 1576
Reputation: 699
These are actually two questions. The part, why your points won't get deleted has already been answered. But for completeness, here the summary:
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
.
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
{
...
}
Upvotes: 2
Reputation: 22038
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.
Upvotes: 1
Reputation: 761
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.
Upvotes: 0