Click here to Skip to main content
Click here to Skip to main content

Derived Class Manager

, 18 Dec 2006
Rate this:
Please Sign up or sign in to vote.
Demonstrates how to manage derived classes using run-time reflection.

Introduction

This article explains the use of the System.Reflection namespace classes to enumerate what classes have been derived from a selected base class at run-time. Instances of the derived classes are then created and stored in an array. The array can then be enumerated for a suitable class instance by an alternative implementation.

This is useful if you wish to derive classes from a selected base and not have to tie them directly into code. Ensuring that they are derived from a single base class allows a minimum implementation to exist.

Using this concept allows generic code to be implemented and modified without having to update any tedious cross references. Being able to derive from a generic base class and then use it generically provides powerful flexibility.

The Theory

The System.Reflection namespace allows the code to be enumerated at run-time. Basically, the code knows about itself, and understands what implementation details exist for the application.

The enumeration of the base class is performed using a simple enumeration of the assembly:

 // Enumerate the system classes, and see which ones are based opon the
// base type we are after...
Assembly myAssembly = Assembly.GetEntryAssembly();

//Get the types..
Type[] myTypes = myAssembly.GetTypes();
//Loop through each type...
foreach(Type currentType in myTypes)
{
 ...
}

This firstly determines what assembly is calling the enumeration method, and then determines what types exist in that assembly. Then, each type is determined. An array of types then exist, and the next stage can be performed, which is determing what class they are derived from and creating an instance of it to store in an array:

// Is it based on transition
if ( currentType.BaseType.Name == "State" )
{
 // yes - create me one of these and store it in the array
 System.Reflection.ConstructorInfo creator =     
    currentType.GetConstructor( new Type[] { } );
 State newState = (State)creator.Invoke( new object[] { } );
 states.Add(newState);
}

The code above firstly determines the base class type of the class (in this case "State"), and then creates an instance of it using the ConstructorInfo class. Finally, the newly created class instance is stored in an array for recalling later.

An Example

To explain this concept, we really need to work on an example to see how useful this might be. For the following example, we're going to use the concept of some system being in selected 'States'. When entering these states, we need to do some processing, and when we leave, we need to free some resources.

A simple base class could be defined as:

using System;

namespace TransitionDemo.States
{
  /// <SUMMARY>
  /// Defines a state of the machine. There isn't many...
  /// </SUMMARY>
  public abstract class State
  {
    /// <SUMMARY>
    /// Defines the name of the state
    /// </SUMMARY>
    private String name = String.Empty;

    /// <SUMMARY>
    /// Defines the name of the state (for diagnostics)
    /// </SUMMARY>
    public String Name { get { return name; } }

    /// <SUMMARY>
    /// Constructor...
    /// </SUMMARY>
    /// <PARAM name="theName">The name of the state</PARAM>
    public State(String theName)
    {
        this.name = theName;
    }

    /// <SUMMARY>
    /// Method called when the state is entered
    /// </SUMMARY>
    public abstract void OnEnter();
        
    /// <SUMMARY>
    /// Method called when the state is left
    /// </SUMMARY>
    public abstract void OnLeave();
  }
}

From this, we will derive a sample class, for now called State1:

using System;

namespace TransactionSample
{
  /// <SUMMARY>
  /// Sample implementation of the State base class.
  /// </SUMMARY>
  public class State1 : TransitionDemo.States.State
  {
    // Event called when the state is entered
    public override void OnEnter()
    {
      System.Windows.Forms.MessageBox.Show("State 1 enetred");
    }

    // Event called when the state is left
    public override void OnLeave()
    {
      System.Windows.Forms.MessageBox.Show("State 1 left");
    }

    // Constructor
    public State1() : base("State 1")
    {
    }
  }
}

Now, let's assume that we want a generic solution that doesn't really understand the derived states, but offers the user the ability to change states. We don't want to hard-code the class type somewhere, what if we want to add more? Things might get messy.

We use a State Manager class - our derived class manager. The code for such a class is shown below:

using System;
using System.Reflection;

namespace TransitionDemo.States
{
  /// <SUMMARY>
  /// Defines the State manager 
  /// </SUMMARY>
  public class DynamicStateManager    
  {
    /// <SUMMARY>
    /// Provides a simple singleton entry
    /// </SUMMARY>
    public static StateManager Manager = new StateManager();

    /// <SUMMARY>
    /// Stores the defined transitions
    /// </SUMMARY>
    private ArrayList states = new ArrayList();

    /// <SUMMARY>
    /// Defines the current state of the system
    /// </SUMMARY>
    private State currentState = null;

    /// <SUMMARY>
    /// Defines the current state name of the system
    /// </SUMMARY>
    public String State
    {
        get 
    { 
      // Default to a blank string                   
          String selectedState = String.Empty;
      // Ensure that the current state is defined
      if ( currentState != null )
        selectedState = currentState.Name;

      // return the selected state name
      return selectedState; 
    }

    set 
    { 
      // Determine if the state exists
      int selectedIndex = GetState(value);
      // Ensure the value is suitable
      if ( selectedIndex >= 0 )
      {
        // call the leave command
        if ( currentState != null )
        currentState.OnLeave;

        // Select the new state
        currentState = states[selectedIndex];

        // And call the entry command
        currentState.OnEnter();
      }
      else
      {
        // Throw an exception 
        throw new InvalidOperationException();
      }
    }
    }

    /// <SUMMARY>
    /// Hidden constructor
    /// </SUMMARY>
    private StateManager()
    {
    // Enumerate the system classes, and see which ones 
    // are based opon the state type...
    Assembly myAssembly = Assembly.GetEntryAssembly();
    
        // Get the types..
    Type[] myTypes = myAssembly.GetTypes();
    
        // Determine which are based on 'State'                    
        foreach(Type currentType in myTypes)
    {
      // is it based on transition
      if ( currentType.BaseType.Name == "State" )
      {
        // yes - create me one of these and store it in the array
         ConstructorInfo creator = currentType.GetConstructor( 
                                            new Type[] { } );
        State newState = (State)creator.Invoke( new object[] { } );
        states.Add(newState);
      }
    }
    }

    /// <SUMMARY>
    ///  Defines the selected index'ed entry
    /// </SUMMARY>
    public State this[int index]
    {
    get { return (State)states[index]; }
    }

    /// <SUMMARY>
    /// Defines the number of registered entries
    /// </SUMMARY>
    public int Count { get { return states.Count; } }

    /// <SUMMARY>
    /// Determines if a transition of states has been defined.
    /// </SUMMARY>
    /// <RETURNS></RETURNS>
    public int GetState( String theState)
    {
    int entryIndex = -1;

    // determine if the transition is registered
    for( int i=0; i < states.Count; i++ )
    {
        // Get the transition
        State state = (State)states[i];
        if ( state.Name == theState )
        {
        entryIndex = i;
        }
    }

    return entryIndex;
    }

    /// <SUMMARY>
    /// Defines if the entry exists
    /// </SUMMARY>
    /// <RETURNS></RETURNS>
    public bool Contains( String theState)
    {
    return ( GetState(theState) != 0 );
    }

  }

}

The state definition is name-driven, meaning that the user could be selecting the state from a list, and it simply gets implemented.

The Demo

The demo expands on this concept, and introduces a TransitionManager that defines what states can be moved between and performs an operation during the transition. This is a good concept when working with state machines.

New states can be added by being derived from a base class, and a transition between the states can be derived from a base class and defines the rules of the transition.

Conclusion

The concept of a derived class manager has only become simple enough to implement efficiently with the introduction of .NET and the System.Reflection namespace. The ability to analyse the code at run-time allows new possibilities in inheritience that would otherwise have to be hard coded.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Eitsop
Software Developer
United Kingdom United Kingdom
Software Developer who generally hacks small applications together, and never quite finishes them...

Comments and Discussions

 
GeneralThis is just what I needed! PinmemberMollyTheCoder2-Mar-10 18:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141029.1 | Last Updated 19 Dec 2006
Article Copyright 2006 by Eitsop
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid