开发者

C# Polymorphism - Accessing Properties of child class that aren't in the parent

I'm running 开发者_Python百科into issues with being able to call functions/properties that are unique to the child class. I'm wondering if there is a way to do do something similar to the following:

public class Creature
{
   public int HealthPoints {get; set;}
   public string Name {get; set;}
   public int AttackValue {get; set;}

   public Creature();
}

public class Magical : Creature
{
   public int Mana {get; set;}
}

So, in this rudimentary example, I'd have a list of Creatures and would need to be able to call from that the Mana property of any given Magical creature.


The short answer:

As you have surely seen in other answers, you can access the Mana property of magical creatures as follows:

foreach (Creature creature in creatures)
{
    if (creature is Magical magical)
    {
        // ... do something with 'magical.Mana' here...
    }
}

Going a step further:

Since we're talking about polymorphism already, you should know that if (x is SomeType) { .. } can be a code smell. Let's say you indeed have a foreach loop for processing all your creatures. It might then be better to create a virtual method in the base class, and do whatever you're doing inside the foreach loop in that virtual method. You would then override the method in your Magical class and thereby get rid of the if statement.

Let's do an example. Let's say, all your creatures get hit and lose some energy. Normal creatures lose health points, while magical creatures lose mana:

public partial class Creature   // rest of your class is omitted in this example
{
    public virtual void TakeHit()
    {
        // whatever you had in your original foreach loop, e.g.:
        HealthPoints--;
    }
}

public partial class Magical : Creature
{
    public override void TakeHit()
    {
        // ... any special processing for Magical creatures goes here, e.g.:
        Mana--;
        // (you could also call 'base.TakeHit()' in this method.)
    }
}

Your original foreach loop now becomes much simpler:

foreach (Creature creature in creatures)
{                        // note: no more if statement -- The virtual method
    creature.TakeHit();  // invocation now takes care of distinguising
}                        // different (sub-)types of Creatures!

I would like to recommend a very nice presentation on this very topic to you: Conditionals and Polymorphism.


So, in this rudimentary example, I'd have a list of Creatures and would need to be able to call from that the Mana property of any given Magical creature.

Yes, this is possible. Use Enumerable.OfType<T>:

IEnumerable<Creature> creatures = new List<Creature>();
// populate creatures
var magicalCreatures = creatures.OfType<Magical>();
foreach(var magicalCreature in magicalCreatures) {
    Console.WriteLine(magicalCreature.Mana);
}


You need to do a conversion to "Magical" at that point. For example:

Creature aCreature = GetMyCreature();
Magical asMagical = aCreature as Magical; // Using "as" operator..
if (asMagical != null)
    Console.WriteLine(asMagical.Mana);


In this specific example you would just need to cast the Creature as Magical, and then call your function. You would then do something like:

((Magical)myCreature).Mana = 0;


if (myCreature is Magical)
{
     var myMagicalCreature = (Magical)myCreature;
     //....access mana, etc.
}


this should do the job:

foreach (var item in collection)
{
    Magical magical = item as Magical;
    if (magical != null)
        magical.Mana = 10;
}       


If a property is unique to the child class then you will have to cast it to be the child class to access the property, e.g.

foreach(Creature creature in creatures)
{
    Magical magical = creature as Magical;
    if (magical != null)
    {
        // Do something with magical.Mana.
    }
}

Or alternatively with LINQ:

foreach(Magical magical in creatures.OfType<Magical>())
{
    // Do something with magical.Mana.
}

It doesn't make sense to do something like:

foreach(Creature creature in creatures)
{
    // Do something with creature.Mana.
}

Because your creature might not be a Magical and therefore wouldn't have Mana.

Polymorphism comes in where you have two different things with the same properties.

foreach(Creature creature in creatures)
{
    // Do something with creature.AttackValue.
}

AttackValue is common to both Creature and Magical so you can happily use it without caring whether it is a Creature or a Magical. And the two could have completely different implementations of AttackValue - you don't care because you are treating them polymorphically.


Inheritance only works one way; all child classes are their parents, but no parent is their child.

To get what you want, you must know the class that you are treating as a Creature is in fact a Magical. You can test this using:

//Uses reflection to examine the type; useful in generic-typed situations
bool isMagical = typeof(T).IsAssignableFrom(Magical); //generic to T where T:Creature

//A keyword-based statement that basically does the same thing given an instance
bool isMagical = creatureInstance is Magical;

If it is feasible, you could provide a non-reflective way to discover this, by declaring something in Creature that must be specified in Magical:

public Class Creature
{
   public int HealthPoints {get; set;}
   public string Name {get; set;}
   public int AttackValue {get; set;}
   public abstract CreatureType Type {get;}

   public Creature();
}

public Class Magical : Creature
{
   public override CreatureType Type {get{return CreatureType.Magical;}}
   public int Mana {get; set;}
}

...

if(creatureInstance.Type == CreatureType.Magical) {...}

Once you know the Creature is a Magical, you can treat the instance as its subclass by casting:

//Direct cast; throws an InvalidCastException if creatureInstance isn't Magical
var mana = ((Magical)creatureInstance).Mana;

//Safe cast - returns null if creatureInstance isn't Magical,
//which in this usage will throw a NullReferenceException anyway
var mana = (creatureInstance as Magical).Mana;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜