从字符串实例化类的最佳性能选项 [英] Best perfomance option to instantiate class from string
问题描述
我在SO上发现了一些问题,这些问题展示了几种从字符串实例化类的方法,我发现的唯一方法是Activator.CreateInstance
.知道不是最快的,我试图找到其他东西并找到了Compiled Expressions
.
I have found some questions here on SO that have shown several methods to instantiate a class from a string, the only one i found how to make work was Activator.CreateInstance
. Knowing it wasn't the fastest i tried to find something else and found Compiled Expressions
.
现在,我如何实现Compiled Expression
以基于给定字符串作为其类型实例化一个新类?有可能吗?
Now, how could i implement a Compiled Expression
to instantiate a new class based on a given string as its type ? Is it possible ?
这是我的代码:
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标记:
Just to give some context, i'm trying to create a custom template builder, the user can input an HTML tag like this one:
<template dir="_Courses" where="active=1" order="name" type="CoursesViewModel"></template>
然后,我将使用数据库中的数据来呈现所选模板.我需要用4个参数实例化CoursesViewModel
:string where, string select, string order, int take
,这些是我的查询过滤参数.
And i will render the selected template with data from my DB. What i need is to instantiate the CoursesViewModel
with 4 parameters: string where, string select, string order, int take
, those are my query filtering parameters.
OBS: I have also tried to work with FastActivator but to use it i have to also use `Type.GetType( "Site.ViewModels." + block.Type ) and i thik that it would end up as costly as my other option, it this right ?
编辑1
我已经使用我的MVC应用程序执行了两个测试,并应用了3种不同的方法,结果以毫秒为单位,并使用了20k次迭代.第三个是使用开关/案例通过以下方法查找正确的类的方法:
I have executed two tests using my MVC application and applied 3 different methods, the results are in miliseconds and where used 20k iterations. The third one is a method that uses a switch/case to look for the correct class by
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是最慢的代码,但在页面刷新中,他的代码工作得更好,而我的switch/case是最快的.任何人都可以解释为什么吗?
I used a PasteBin to share the code. Oddly the methods have worked differently on each case, on the first execution @Ivan Stoev was the slowest code but on the page refresh his code worked better and my switch/case was the fastest. Could anyone please explain why this ?
编辑2
这些测试实现了Ivan Stoev代码的更改版本,其中将Dictionary更改为ConcurrentDictionary,并使用参数实现Activator.
These tests implemented the changed version of Ivan Stoev's code where the Dictionary was changed to ConcurrentDictionary and the Activator was implemented with parameters
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
这是修改了代码的垃圾箱: PasteBin
Here is the bin with the modified code: PasteBin
推荐答案
您可以使用以下帮助程序类,但需要自己衡量性能:
You can use the following helper class, but you need to measure the performance yourself:
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正确提及的那样,可以通过将Dictionary
和Monitor
锁替换为
UPDATE: As @xanatos correctly mentioned, the above can be improved by replacing the Dictionary
and Monitor
locks with ConcurrentDictionary:
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);
}
}
这篇关于从字符串实例化类的最佳性能选项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!