c # подключаемая фабрика / статическая инициализация - PullRequest
2 голосов
/ 03 февраля 2012

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

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 и запуске программы.

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

Ответы [ 2 ]

1 голос
/ 03 февраля 2012

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

0 голосов
/ 11 ноября 2014

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

    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% времени выполнения пути критического кода.

ОДНАКО: из-за ограничений производительности, для части моего кода требовалась другая схема построения, которая быстро перестраивалась (потенциально миллионам объектов, требовалось время отклика менее секунды). (см. обсуждение в 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)));
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...