c#pluggable factory / static initialization

时间:2012-02-03 06:47:11

标签: c# static

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

可插拔工厂的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加载和程序启动时。

  3. 是否可以这样做而不必采用这些方法?

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分: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)));
        }