How To Determine What Properties An Object Has At Runtime With .NET Using Reflection

One of the neat things about the .NET Framework is the System.Reflection namespace. In Microsoft's words, it "contains classes and interfaces that provide a managed view of loaded types, methods, and fields, with the ability to dynamically create and invoke types." In other words, you can interact with and manipulate objects at runtime without having any foreknowledge of what the objects actually are.

I'm currently working on two games in my spare time in which this functionality could come in handy. In particular, I'm developing a strategy game based on Battlestar Galactica (the original series, not the remake). The interface includes a star map where the player can view planetary systems and spacecraft. Part of the plan for the game is to allow the player to double-click on any of the ships which are visible on the map and be presented with a window displaying the ship's stats. However, all ships are not created equal: I have a Spacecraft base class, and several sub-classes which derive from it (such as FighterSquadron and BaseShip). These sub-classes extend the base class and add their own unique properties. For example, the BaseShip class contains a collection of FighterSquadrons. Because each class can contain additional properties that the other ship classes do not, a one-size-fits-all approach isn't viable, unless, of course, I add some code to accomodate the various classes. But this would require some hard-coding, and wouldn't be very flexible -- if I ever added another ship class, I would need to add code to the ship viewing form to accomodate it.

The solution to this problem lies within the System.Reflection namespace. Using this namespace, I've created a simple form which takes an object as a parameter to its constructor. The constructor, in turn, passes the object to a function named GetObjectProperties which uses Reflection to determine the object's type and add the names and values of all of its properties to a list box. The code below illustrates this (a using System.Reflection; declaration is implied):

protected void GetObjectProperties(object obj)
{
   Type type = null;
   PropertyInfo[] props = null;

   try
   {
      //Get the type of the object passed in to this function
      type = obj.GetType();

      //Get the properties of the object
      props = type.GetProperties(BindingFlags.Public|BindingFlags.Instance);

      /*
      Loop through each property in the properties array and add its name
      and value to the form's ListBox
      */
      foreach(PropertyInfo prop in props)
      {
      lstProperties.Items.Add(prop.Name + ": " + type.InvokeMember( prop.Name, BindingFlags.GetProperty, null, obj, null ));
      }
   }
   catch(Exception ex)
   {
      MessageBox.Show("An error occurred while trying to get object properties: " +
      ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
   }
   finally
   {
      //Clean up
      type = null;
      props = null;
   }
}

As you can see, the function takes a parameter of type object as its sole parameter. Because all objects in .NET derive from System.Object (of which the type object is an alias), we can pass any of our Spacecraft classes to this function. The function in turn determines its type by calling the GetType() method on it to instantiate a type object. Using the type object, we get all of the properties for this particular instance of the object, which we then loop through, adding their names and properties to the ListBox control on the form. The output looks like this:


Of course, I'll jazz it up for use with the game -- this is just a proof-of-concept version; the game version will implement the visual theme of the game's interface, as well as display an image of the ship. But even this simple version of the form illustrates the usefulness of Reflection. In fact, with this same form, I could also allow the player to view information about planetary systems, and any other objects represented on the game map. And the usefulness doesn't end there -- you can also invoke methods on objects using Reflection, even if you don't know the object's type ahead of time.

Comments

Popular Posts

Resolving the "n timer(s) still in the queue" Error In Angular Unit Tests

How to Get Norton Security Suite Firewall to Allow Remote Desktop Connections in Windows

How to Determine if a Column Exists in a DataReader

Silent Renew and the "login_required" Error When Using oidc-client

Fixing the "Please add a @Pipe/@Directive/@Component annotation" Error In An Angular App After Upgrading to webpack 4