Jonathon Reinhart
Jonathon Reinhart

Reputation: 137398

Using reflection to get static property value, a concatenation of derived and base class

I'm going to do my best to explain my vision here. This is a very lame made-up example. I've got a few different types of Bags, and they all hold their own special type of Marble. Each type of Marble has its own set of Nicknames (strings).

Unfortunately, there are other things besides the Marble in the Bag, so generics won't help me here.

// Bags
abstract class Bag {
    protected Type MarbleType { get; }
    protected List<Marble> _marbles;

    public void DumpBag()
    { ... }
}
class RedBag : Bag {
    override Type MarbleType { get { return typeof(RedMarble); } }
}
class BlueBag : Bag {
    override Type MarbleType { get { return typeof(BlueMarble); } }
}

// Marbles
abstract class Marble {
    public static IEnumerable<string> Nicknames {
        get {
            return new List<string>() {
               "Marble", "RollyThing"
            }
        }
    }
}
class RedMarble : Marble {
    public static IEnumerable<string> Nicknames {
        get {
            return new List<string>(Marble.Nicknames) {
                "Ruby"
            };
        }
    }
}
class BlueMarble : Marble { ... }

So now we get to the details, the implementation of DumpBag(). Consider the following call:

Bag b = new RedBag();
b.GetMarbles();
b.DumpBag();

I would like it to print:

Bag of Marbles (aka "Marble", "RollyThing", Ruby"):
- Marble 1
- Marble 2
...

We see that, in order to print that heading, the Bag must be able to have knowledge of the derived type of Marble, independent of any actual instances. It gets a concatenation of the Nicknames of the Marble base class, but also the derived RedMarble.

DumpBag needs to do a kind of 'static virtual call'. I've started implementing DumpBag with the following:

public void DumpBag() {
    PropertyInfo pi = this.MarbleType.GetProperty("Nicknames", BindingFlags.Static);
    IEnumerable<string> nicknames = pi.GetValue(null, null);  // No instance

    StringBuilder sb = new StringBuilder("Bag of Marbles (aka ");
    foreach (string nn in nicknames)
        sb.Append("\"" + nn + "\", ");
    Console.WriteLine(sb.ToString());

    ...
}

My questions:

  1. Is this sane? Hopefully I have (or I can) explain my rationale for why I've gone this route.
  2. I get a warning (of course) that RedMarble.Nicknames hides Marble.Nicknames. Does it seem valid to go ahead and mark it new?

Upvotes: 3

Views: 5082

Answers (1)

yamen
yamen

Reputation: 15618

You'll find all you're missing is an explicit cast:

(List<string>)this.MarbleType.GetProperty("Nicknames").GetValue(null, null);

This worked fine for me when I tested it.

And as discussed in the comments, no you shouldn't be using the new keyword really, you're better off naming the base class static method to something else so there is no ambiguity. You are after all in control of this and not using someone else's code.


Now, should you do it this way?

Well, first surely you want to use generics not defined methods to return types:

abstract class Bag<T> where T:marble {
    public void DumpBag()
    { 
        // here you can use
        // (IEnumerable<string>)typeof(T).GetProperty("Nicknames").GetValue(null, null);
    }
}

class RedBag : Bag<RedMarble> {
}

class BlueBag : Bag<BlueMarble> {
}

Of course the second thing you could do is make this not static, in which case the property will be abstract in Marble, and overridden in RedMarble and BlueMarble, and then just accessed in DumpBag directly as Nicknames rather than using reflection.

Upvotes: 3

Related Questions