我在SO上发现了一些问题,这些问题已经展示了几种从字符串中实例化类的方法,我发现如何创建工作的唯一方法是Activator.CreateInstance
。知道它不是最快的我试图找到其他东西并发现Compiled Expressions
。
现在,我如何实现一个Compiled Expression
来实例化一个基于给定字符串作为其类型的新类?有可能吗?
这是我的代码:
public List<HtmlBlock> ParseBlocks( Page page, ControllerContext controller )
{
var nodeBlocks = GetNodes( page.html );
var blocks = new List<HtmlBlock>();
Parallel.ForEach( nodeBlocks, block => blocks.Add( ParseNode( block, controller ) ) );
return blocks;
}
private static HtmlBlock ParseNode( HtmlBlock block, ControllerContext controller )
{
try
{
//Instantiate the class
var type = Activator.CreateInstance( null, "Site.ViewModels." + block.Type );
//Populate selected template
block.SetHtml( new HelperController().RenderView( block.Template, type.Unwrap(), controller ) );
return block;
}
//Suppress any error since we just want to hide the block on parse error
catch (Exception)
{
block.SetHtml( "" );
return block;
}
}
为了给出一些上下文,我试图创建一个自定义模板构建器,用户可以输入像这样的HTML标记:
<template dir="_Courses" where="active=1" order="name" type="CoursesViewModel"></template>
我将使用来自我的数据库的数据渲染所选模板。我需要的是用{4}参数实例化CoursesViewModel
:string where, string select, string order, int take
,这些是我的查询过滤参数。
OBS :我也尝试使用FastActivator但是要使用它我还必须使用`Type.GetType(&#34; Site.ViewModels。&#34; + block.Type)和我认为它最终会像我的其他选项一样昂贵,这是对的吗?
编辑1
我使用我的MVC应用程序执行了两个测试并应用了3种不同的方法,结果以毫秒为单位,并且使用了20k次迭代。第三个是使用开关/案例通过
查找正确类的方法1) ViewModelFactory.CreateInstance("NameSpace.ClassName", "", "", "", 0)
2) Activator.CreateInstance(null, "NameSpace.ClassName")
3) HtmlParser.GetClassType("ClassName")
------------------------------------------------------------------------
1st Test 2nd Test | 20k
1) 93068 | 110499
2) 117460 | 89995
3) 82866 | 77477
我使用PasteBin来分享代码。奇怪的是,这些方法在每种情况下的工作方式不同,在第一次执行时,@ Ivan Stoev是最慢的代码,但在页面刷新时,他的代码工作得更好,我的开关/案例是最快的。有人可以解释一下为什么会这样吗?
编辑2
这些测试实现了Ivan Stoev代码的更改版本,其中Dictionary已更改为ConcurrentDictionary,Activator使用参数实现
1) ViewModelFactory.CreateInstance( "ClassName", "", "", "", 0 )
2) var type = Type.GetType( "NameSpace.ClassName" );
var obj = Activator.CreateInstance( type, new object[] { "", "", "", 0 } );
3) HtmlParser.GetClassType("ClassName")
------------------------------------------------------------------------
1st Test 2nd Test | 200k
1) 3418 | 3674
2) 5759 | 5859
3) 3776 | 4117
以下是包含修改后代码的bin:PasteBin
答案 0 :(得分:1)
仅仅因为Ivan已经编写了基本代码,我将基于Expression树发布等效代码。它在生成甚至执行时都可能更慢。
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
public static class ViewModelFactory
{
static readonly Type[] arguments = { typeof(string), typeof(string), typeof(string), typeof(int) };
static readonly ConcurrentDictionary<string, Func<string, string, string, int, object>> factoryCache = new ConcurrentDictionary<string, Func<string, string, string, int, object>>();
public static object CreateInstance(string typeName, string where, string select, string order, int take)
{
Func<string, string, string, int, object> factory;
lock (factoryCache)
{
if (!factoryCache.TryGetValue(typeName, out factory))
{
var type = Type.GetType(typeName);
var ci = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, arguments, null);
ParameterExpression par1 = Expression.Parameter(typeof(string), "par1");
ParameterExpression par2 = Expression.Parameter(typeof(string), "par2");
ParameterExpression par3 = Expression.Parameter(typeof(string), "par3");
ParameterExpression par4 = Expression.Parameter(typeof(int), "par4");
var exprNew = Expression.New(ci, par1, par2, par3, par4);
var lambda = Expression.Lambda<Func<string, string, string, int, object>>(exprNew, par1, par2, par3, par4);
factory = lambda.Compile();
factoryCache.Add(typeName, factory);
}
}
return factory(where, select, order, take);
}
}
答案 1 :(得分:0)
您可以使用以下帮助程序类,但您需要自己测量性能:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
public static class ViewModelFactory
{
static readonly Type[] arguments = { typeof(string), typeof(string), typeof(string), typeof(int) };
static readonly Dictionary<string, Func<string, string, string, int, object>>
factoryCache = new Dictionary<string, Func<string, string, string, int, object>>();
public static object CreateInstance(string typeName, string where, string select, string order, int take)
{
Func<string, string, string, int, object> factory;
lock (factoryCache)
{
if (!factoryCache.TryGetValue(typeName, out factory))
{
var type = Type.GetType(typeName);
var ci = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, arguments, null);
var dm = new DynamicMethod("Create" + typeName, type, arguments, true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Ldarg_3);
il.Emit(OpCodes.Newobj, ci);
il.Emit(OpCodes.Ret);
factory = (Func<string, string, string, int, object>)dm.CreateDelegate(
typeof(Func<string, string, string, int, object>));
factoryCache.Add(typeName, factory);
}
}
return factory(where, select, order, take);
}
}
更新:正如@xanatos正确提到的那样,可以使用ConcurrentDictionary替换Dictionary
和Monitor
锁来改进上述内容:
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Reflection.Emit;
public static class ViewModelFactory
{
static readonly Type[] arguments = { typeof(string), typeof(string), typeof(string), typeof(int) };
static readonly ConcurrentDictionary<string, Func<string, string, string, int, object>>
factoryCache = new ConcurrentDictionary<string, Func<string, string, string, int, object>>();
static readonly Func<string, Func<string, string, string, int, object>> CreateFactory = typeName =>
{
var type = Type.GetType(typeName);
var ci = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, arguments, null);
var dm = new DynamicMethod("Create" + typeName, type, arguments, true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Ldarg_3);
il.Emit(OpCodes.Newobj, ci);
il.Emit(OpCodes.Ret);
return (Func<string, string, string, int, object>)dm.CreateDelegate(
typeof(Func<string, string, string, int, object>));
};
public static object CreateInstance(string typeName, string where, string select, string order, int take)
{
var factory = factoryCache.GetOrAdd(typeName, CreateFactory);
return factory(where, select, order, take);
}
}