派生类管理器






2.50/5 (2投票s)
2006年12月19日
3分钟阅读

15703

120
演示如何使用运行时反射来管理派生类。
引言
本文解释了如何使用 System.Reflection
命名空间中的类来枚举在运行时从选定的基类派生了哪些类。 然后创建派生类的实例并将其存储在数组中。 然后可以枚举该数组,通过另一种实现方式找到合适的类实例。
如果您希望从选定的基类派生类,而不必将它们直接绑定到代码中,这将非常有用。 确保它们是从单个基类派生的,可以保证存在最小的实现。
使用此概念允许实现和修改通用代码,而无需更新任何繁琐的交叉引用。 能够从通用基类派生,然后以通用方式使用它,提供了强大的灵活性。
理论
System.Reflection
命名空间允许在运行时枚举代码。 基本上,代码了解自身,并且理解应用程序存在的哪些实现细节。
基类的枚举是使用简单的程序集枚举来执行的。
// 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)
{
...
}
首先确定哪个程序集正在调用枚举方法,然后确定该程序集中存在哪些类型。 然后,确定每个类型。 然后存在一个类型数组,并且可以执行下一个阶段,即确定它们是从哪个类派生的,并创建它的一个实例以存储在数组中。
// 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);
}
上面的代码首先确定类的基类类型(在本例中为“State
”),然后使用 ConstructorInfo
类创建它的实例。 最后,新创建的类实例被存储在一个数组中,以便稍后调用。
一个示例
为了解释这个概念,我们真的需要研究一个例子,看看这可能有多有用。 对于下面的例子,我们将使用某个系统处于选定的“状态”的概念。 当进入这些状态时,我们需要做一些处理,当我们离开时,我们需要释放一些资源。
可以定义一个简单的基类,如下所示
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();
}
}
由此,我们将派生一个示例类,暂时称为 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")
{
}
}
}
现在,假设我们需要一个通用的解决方案,它实际上不了解派生状态,但为用户提供了更改状态的能力。 我们不想在某个地方硬编码类类型,如果我们要添加更多呢? 事情可能会变得混乱。
我们使用一个 State Manager 类 - 我们的派生类管理器。 这种类的代码如下所示
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 );
}
}
}
状态定义是名称驱动的,这意味着用户可以从列表中选择状态,并且它只是简单地被实现。
演示
该演示扩展了这一概念,并引入了一个 TransitionManager
,它定义了可以在哪些状态之间移动,并在转换期间执行操作。 这是使用状态机时的一个好概念。
可以通过从基类派生来添加新状态,并且状态之间的转换可以从基类派生并定义转换的规则。
结论
派生类管理器的概念直到 .NET 和 System.Reflection
命名空间的引入,才变得足够简单以有效地实现。 在运行时分析代码的能力允许在继承中出现新的可能性,否则这些可能性必须被硬编码。