从字符串实例化类的最佳性能选项 [英] Best perfomance option to instantiate class from string

查看:39
本文介绍了从字符串实例化类的最佳性能选项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在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 :我也曾尝试与

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正确提及的那样,可以通过将DictionaryMonitor锁替换为

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆