Derived Class Manager






2.50/5 (2 votes)
Dec 19, 2006
3 min read

15480

120
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>
/// The name of the state
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.