2012-02-03 2 views
2

Я ищу C# equivelent моего кода для подключаемой фабрики. Преимущество связанного метода состоит в том, что статическая инициализация приводит к операции push, где каждый плагин добавляет себя к фабрике.C# pluggable factory/статическая инициализация

C++ код для сменного завода: (http://codepad.org/7pgzaaAK)

// base class for plugins 
class Foo{ 
public: 
    virtual std::string getName()const=0; 
    virtual void exercise()const=0; 
}; 

// plugin factory 
class FooFactory{ 
    public: 
    static Foo* GetA(std::string s); 
    typedef std::map<std::string,Foo*(*)(void)> mapType; 
    static mapType& getA(); 
}; 

FooFactory::mapType& FooFactory::getA(){static mapType getA;return getA;} 
Foo* FooFactory::GetA(std::string s) 
{return getA().find(s)!=getA().end()?getA()[s]():0;} // to simplify access 

// helper function to add the fun 
template<typename T> 
Foo* getNew(){ return new T; } 

// use the CRTP to automatically register the object with the factory. 
template <typename T> struct Reg { Reg() { /*cout << "registering a " << T().getName(<<endl;*/ 
FooFactory::getA().insert(std::pair<std::string, Foo*(*)()>(T().getName(), &getNew<T>));} }; 

template <typename T> 
class Foo_reg:public Foo{ 
public: 
    Foo_reg(){&reg;}; // using a reff to the static is enough to force initialization of the static earlier 
    static Reg<T> reg; 
}; 
template <typename T> Reg<T> Foo_reg<T>::reg; 

///////////////// 
class FooBar:public Foo_reg<FooBar>{ // automatic registration with the factory 
public: 
    FooBar(){a=10;} 
    virtual std::string getName()const{return "Foo Bar";} 
    virtual void exercise()const {cout <<a;} 
private: 
    int a; 
}; 

// exercise the factory and objects. 
int main(){ 
    Foo* foo=FooFactory::GetA("Foo Bar"); 
    foo->exercise(); 
} 

В C# я могу видеть 2 способа сделать это, оба из них вытаскивать операции

  1. Есть явное преднастроенным список всех плагинов, который должен поддерживаться отдельно от самих плагинов.

  2. Используйте отражение кода для итерации по всем типам, проверяя, являются ли они допустимыми для Foo и инициализируют их статику, при любой загрузке dll и запуске программы.

Возможно ли это сделать, не прибегая к этим методам?

+1

как в стороне , должен ли я включать код кодекса здесь или там? –

+0

Как правило, да, это не помешает включать фрагменты кода в вопросы. SO имеет теги форматирования для кода, так что он отлично отображает –

+0

для отзыва - отредактированный фрагмент кода. –

ответ

1

Я бы не беспокоился о отражении. Даже PEX не имеет проблем с этим. Проще говоря, по рефлексии вы просто проверяете метаданные сборки и видите, определяли ли они какие-либо классы, которые реализуют определенный интерфейс или помечены определенным атрибутом, это очень быстро! В любом случае, CLR никогда не будет запускать код, который не называется явно, так что нет, вам нужно прибегнуть к какому-то механизму выталкивания (даже если вы сделаете его похожим на механизм push)

0

, понимая, что единственное реальное требование здесь было то, что в конце концов у нас есть поиск между идентификатором типа (строка/тип) и функцию, которая позволяет нам получить экземпляр этого типа я в конечном итоге решения этого со следующей схемой:

private static readonly Dictionary<string, KeyValuePair<TConstructor, Node>> Types = 
     new Dictionary<string, KeyValuePair<TConstructor, Node>>(); 
    private static readonly Dictionary<Type, string> classNameMap = new Dictionary<Type, string>(); 

    private class constructableNode<T> where T : Node, new() 
    { 
     public constructableNode() 
     { 
      var t = new T(); 
      Types[t.Type()] = new KeyValuePair<TConstructor, Node>(thisTConstructor, t); 
      classNameMap[typeof (T)] = t.Type(); 
     } 

     private static T thisTConstructor() 
     { 
      var t = new T(); 
      return t; 
     } 
    } 

    public static Node GetA(string s) 
    { 
     if (Types.ContainsKey(s) == false) 
     { 
      UpdateAvailableTypes(); 
     } 
     if (Types.ContainsKey(s) == false) 
     { 
      throw new BadNodeType(s); 
     } 
     // look up the correct constructor, and call it. 
     return Types[s].Key(); 
    } 

    public static void UpdateAvailableTypes() 
    { 
     Assembly targetAssembly = Assembly.GetExecutingAssembly(); // or whichever - could iterate dlls in the plugins folder or something 
     UpdateAvailableTypes(targetAssembly); 
     classNameMap[typeof (Node)] = "BaseNode"; // HARD CODED INTO the node type itself also 
    } 

    private static void UpdateAvailableTypes(Assembly targetAssembly) 
    { 
     IEnumerable<Type> subtypes = targetAssembly.GetTypes().Where(t => t.IsSubclassOf(typeof (Node))); 
     Type nodeConstructor = typeof (constructableNode<>); 
     foreach (Type currentType in subtypes) 
     { 
      // this line throwing an error means that the Node type does not have an empty constructor. 
      Activator.CreateInstance(nodeConstructor.MakeGenericType(currentType)); 

     } 
    } 

Это простой метод, но стоимость динамического вызова каждого вызова немного крутая по сравнению с другими параметрами, и когда я впервые использовал этот шаблон для раздела ниже, это было 80% от времени выполнения критического кода.

HOWEVEVER: Из-за ограничений производительности для части моего кода, который был перестроен быстро (потенциально миллионы объектов, требуется время ответа второй секунды), потребовался другой шаблон конструкции. (См обсуждения на http://blogs.msdn.com/b/haibo_luo/archive/2005/11/17/494009.aspx для различных методов построения на основе отражения)

для них я нужна следующая конструкции парадигма, в результате чего промежуточный поиск через вызов функции к обобщенному классу вымывало

static buildCostItems() 
     { 
      //from http://blogs.msdn.com/b/haibo_luo/archive/2005/11/17/494009.aspx 
      AssemblyBuilder asmBldr = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("inmemory"), 
                        AssemblyBuilderAccess.Run); 
      ModuleBuilder modBldr = asmBldr.DefineDynamicModule("helper"); 
      TypeBuilder typeBldr = modBldr.DefineType("ClassFactory"); 
      Type tci = typeof (CostsItem); 
      IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes().Where(tci.IsAssignableFrom); 
//// Note -- assumption of currently executing assembly -- this isn't a requirement, but didn't need the dynamic callback capabilities of the Node constructor here. 
      List<Type> enumerable = types as List<Type> ?? types.ToList(); 
      foreach (Type type in enumerable) 
      { 
       MethodBuilder methBldr = typeBldr.DefineMethod(type.Name, 
                   MethodAttributes.Public | MethodAttributes.Static, type, 
                   new[] {typeof (CostsItem)}); 
       ILGenerator ilgen = methBldr.GetILGenerator(); 
       ilgen.Emit(OpCodes.Nop); 
       ilgen.Emit(OpCodes.Ldarg_0); 
       ilgen.Emit(OpCodes.Newobj, type.GetConstructor(new[] {typeof (CostsItem)})); 
       ilgen.Emit(OpCodes.Ret); 
      } 
      Type baked = typeBldr.CreateType(); 
      foreach (Type type in enumerable) 
       ctors.Add(type, 
          (CtorCloneDelegate) 
          Delegate.CreateDelegate(typeof (CtorCloneDelegate), baked.GetMethod(type.Name))); 
     } 
Смежные вопросы