【问题标题】:How do I declare a list of types, and later instantiate one?如何声明类型列表,然后实例化一个?
【发布时间】:2011-08-11 23:26:03
【问题描述】:

我有一个案例,我有一些类,每个类都处理一种令牌。它们都来自一个基本的处理程序类(稍微简化了一点,所以请原谅任何反编译的错别字):

public abstract class TokenClassBase
{
  public static bool HandlesTokenType(TokenKind AType)
  {
    return handledTokens.Contains(AType);
  }
  protected virtual void HandleToken(AToken)
  {
  }
}

public class TokenClass1 : TokenClassBase
{
  public static new bool HandlesTokenType(TokenKind AKind)
  {
    return AKind == TokenKind.type1;
  }
  public override void HandleToken(AToken)
  {
  //do some work
  }

}
// public class TokenClass2... etc

我还有一个工作类,我想在其中存储这些处理程序的列表,然后实例化其中一个处理程序来处理令牌:

public class MyWorker
{
  private List<Type> handlers;
  public MyWorker()
  {
    handlers = new List<Type>;
    handlers.Add(typeof(TokenClass1));
    handlers.Add(typeof(TokenClass2));
    //... etc
  }
  protected virtual void HandleToken(AToken)
  {
    foreach (TokenBaseClass handler in handlers)
    {
      if (handler.HandlesToken(AToken))
      {
        instantiate(handler);
        handler.HandleToken(AToken);
        break;
      }
    }
  }
}

我的问题是,我如何处理最后一个 foreach?这甚至可能 - 还是有更好的方法?我喜欢将来能够添加新类型的可扩展性,只需将它们添加到处理程序列表(甚至从外部传入它们)。 我正在使用 c#,框架 3.5+。

【问题讨论】:

    标签: c# class list types reference


    【解决方案1】:

    不可能这样,不 - 因为您的列表是 types 的列表,而不是对该类型的 instances 的引用。

    假设每种类型都有一个无参数构造函数,您可以轻松获得的最接近的是:

    foreach (Type handlerType in handlers)
    {
       // Create an instance of the handler type
       TokenBaseClass handler = 
           (TokenBaseClass) Activator.CreateInstance(handlerType);
       if (handler.HandlesToken(AToken))
       {
         handler.HandleToken(AToken);
         break;
       }
    }
    

    编辑:在回答您在 cmets 中的问题时,我的处理方式会略有不同。

    我会将您的 List&lt;Type&gt; 更改为 List&lt;Func&lt;TokenKind, TokenClassBase&gt;&gt;。换句话说,工厂函数列表,从TokenKindTokenClassBase。每种类型的函数将取决于类型,但它要么返回TokenClassBase的实例,如果TokenKind不能是null处理。

    然后你会使用:

    foreach (var handlerFactory in handlerFactories)
    {
        TokenBaseClass handler = handlerFactory(AToken);
        if (handler != null)
        {
            handler.HandleToken(AToken);
            break;
        }
    }
    

    创建委托的方式取决于代码的确切性质,但您可以使用 lambda 表达式或方法组转换,可能来自静态方法。

    【讨论】:

    • 您也可以让处理程序实现 ICloneable(如果您想根据“服务请求”克隆一个实例)
    • @sehe:是的,但您仍然需要一个实例列表,而不是类型列表。
    • 啊哈,我误读了您的评论,并假设类型已经由它们的实例存储。读得太快了,sry。仍然 ICloneable 在这里响起主题:)
    • 啊,Activator.CreateInstance 是我想要替换我的“实例化(处理程序)”行的东西。有没有办法在不这样做的情况下调用类中的静态方法(HandlesToken)?本质上我想在实例化它之前问“这个类可以处理任务吗”。
    • @Jon:谢谢,这提供了一个很好的方向。我对使用静态方法的引用填充 handlerFactories 列表的最后一步有点模糊;你的意思是我会使用 handlerFactories.Add(TokenKind.type1, TokenClass1.HandleToken) ?
    【解决方案2】:

    您可能想查看Activator.CreateInstance 方法。

    你的循环是

    foreach (Type handlerType in handlers)
    {
      if (...not sure how you'd do this part...)
      {
        TokenBaseClass handler = (TokenBaseClass)Activator.CreateInstance(handlerType);
        handler.HandleToken(AToken);
        break;
      }
    }
    

    对于缺少的部分,您基本上需要一份每种类型可以处理的令牌的列表。如果是 1-1 关系,您可以考虑使用Dictionary&lt;TokenKind,Type&gt;。或者,要完全避免反射,请使用 Dictionary&lt;TokenKind,Func&lt;TokenClassBase&gt;&gt;,每个值为 () =&gt; new TokenClass1()

    为什么你想要一个类型列表而不是一个实例列表?

    【讨论】:

      猜你喜欢
      • 2017-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-28
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 2015-04-05
      相关资源
      最近更新 更多