从字符串

时间:2016-03-14 18:39:34

标签: c# linq

我在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}参数实例化CoursesViewModelstring 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

2 个答案:

答案 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替换DictionaryMonitor锁来改进上述内容:

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);
    }
}