понимая, что единственное реальное требование здесь заключалось в том, что в конце дня у нас есть поиск между идентификатором типа (строка / тип) и функцией, которая позволяет нам получить экземпляр того типа, который я в итоге решил, следующий шаблон:
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)));
}