65.9K
CodeProject 正在变化。 阅读更多。
Home

派生类管理器

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.50/5 (2投票s)

2006年12月19日

3分钟阅读

viewsIcon

15703

downloadIcon

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 命名空间的引入,才变得足够简单以有效地实现。 在运行时分析代码的能力允许在继承中出现新的可能性,否则这些可能性必须被硬编码。

© . All rights reserved.