简体   繁体   中英

Looping through a parent / Child structure

I'm working on a game where it will be possible to target each mob's limbs specifically, so you could aim for the head, the legs, ...

I have this constructor:

public Humanoid(Race race, Gender gender, string firstname, string lastname = null)
{
    this.Legs = new List<Leg> { new Leg(), new Leg() };
    this.Torso = new Torso();
    this.Arms = new List<Arm> { new Arm(), new Arm() };
    this.Heads = new List<Head>
    {
        new Head
        {
            Ears = new List<Ear> { new Ear(), new Ear() },
            Eyes = new List<Eye> { new Eye(), new Eye() }
        }
    };
}

All of these limbs inherit from interface ILimb .

What is the best way to be able to loop through all limbs, including childs (when applicable)?

I could add a protected List<ILimb> { get; set; } protected List<ILimb> { get; set; } protected List<ILimb> { get; set; } and then add each, but that's redundant.

Any ideas or suggestions for improvement?

The best way would be to have this structure instead:

public Humanoid(Race race, Gender gender, string firstname, string lastname = null)
{
    this.Limbs = new List<ILimb>();
    this.Limbs.Add(new Legs() { Limbs = new List<Limb>() { new Leg(), new Leg() });
    this.Limbs.Add(new Torso());
    this.Limbs.Add(new Arms() { Limbs = new List<Limb>() { new Arm(), new Arm() });
    this.Limbs.Add(new Heads() { Limbs = new List<Limb>() { new Head() { Limbs = new List<Limb>() .... , ... });
}

you can tidy the code up but basically it should have a collection of limbs, and limbs should have collections of limbs so that you can have Head > Ears > Ear or whatever hierarchy you wish.

Then in your ILimb interface, give it a Limbs property

public interface ILimb
{
    List<ILimb> Limbs { get; set; }
    List<ILimb> GetAllLimbs { get; }
}

And create an abstract base class Limb with this method:

public virtual GetAllLimbs()
{
// pseudocode: something like this (basically recurse through the children)
return this.Limbs().foreach (c => c.GetAllLimbs()).union(this.Limbs());
}

Then it can crawl down the hierarchy easily and retrieve every limb.

So you could do

myHumanoid.GetAllLimbs().Where(c => c is Arm).TakeDamage(5);

for example.

You have custom models for all of your objects except one... a custom collection of models. List<T> is a good start, but it doesn't have the functionality you're looking for. Functionality you're trying to put into Humanoid but doesn't really belong there.

Implement something like this:

public class LimbList<T> : IList<T> where T : ILimb
{
    // implement IList<T> here
}

Here you would include business logic for the collection of limbs. For example:

  • If there are already 2 Arm objects in the backing collection, throw an exception when calling .Add() with an Arm object.
  • If there is already 1 Torso object in the backing collection, throw an exception when calling .Add() with a Torso object.
  • etc.

A Humanoid would then have a LimbList<ILimb> property:

public Humanoid(Race race, Gender gender, string firstname, string lastname = null)
{
    this.Limbs.Add(new Leg());
    this.Limbs.Add(new Leg());
    this.Limbs.Add(new Torso());
    this.Limbs.Add(new Arm());
    this.Limbs.Add(new Arm());
    this.Limbs.Add(new Head
        {
            // as an added exercise, how would you extend this concept to the Head object?
        });
}

And you can loop over that list easily:

foreach (var limb in this.Limbs)

Essentially the point here is that a collection of objects is itself an object, with custom logic like any other object. Put object logic in the objects, put collection logic in the collections. There's no rule that says you must only use the built-in collections in the framework.

Given a legs list and an arms list, you can do this:

IEnumerable<ILimb> limbs = ((IEnumerable<ILimb>)arms).Concat(legs);

And then iterate it:

foreach (var limb in limbs) 
{
    // limb is an ILimb and you can access anything in that interface 
    // for each limb
}

Another alternative is you could add a method to Humanoid like this:

public IEnumerable<ILimb> GetLimbs() 
{
    foreach (var a in Arms)
    {
        yield return a;
    }
    foreach (var l in Legs)
    {
        yield return l;
    }
}

Then you could do something like:

foreach(var limb in someHumanoid.GetLimbs()) 
{

}

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