c#可插拔工厂/静态初始化

本文关键字:静态 初始化 工厂 | 更新日期: 2023-09-27 18:22:24

我正在为一个可插拔工厂寻找一个c#等效代码。链接方法的优点是静态初始化会导致推送操作,每个插件都会将自己添加到工厂中。

可插拔工厂的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#中,我可以看到两种方法,它们都是拉操作

  1. 有一个明确的所有插件的预构建列表,它必须与插件本身单独维护。

  2. 使用代码反射迭代所有类型,检查它们是否可移植到Foo,并在任何dll加载和程序启动时初始化它们的静态。

有没有可能做到这一点而不必求助于这些方法?

c#可插拔工厂/静态初始化

我不会太担心反射。即使是PEX使用它也没有问题。简单地说,通过反射,你只需要检查程序集的元数据,看看它是否定义了任何实现某个接口的类或标记了某个属性,这非常快!无论如何,CLR永远不会运行没有被显式调用的代码,所以不,你必须求助于某种拉机制(即使你让它看起来像推机制)

意识到这里唯一真正的要求是在一天结束时,我们在类型标识符(string/type)和允许我们获得该类型实例的函数之间进行查找,我最终用以下模式解决了这个问题:

    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));
        }
    }

这是一个简单的方法,但与其他选项相比,动态Invoke每次调用的成本有点高,当我第一次在下面的部分中使用这种模式时,它是关键代码路径运行时的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)));
        }