Activator.CreateInstance 方法创建对象和Expression Tree创建对象性能的比较(构造函数含多参数的情况)
2009-12-09 20:07
573 查看
前言
标题实在是太长了,是吗?其实并无什么高深的内容。今天看了 Will Meng 的 《Activator.CreateInstance(Type type)方法创建对象和Expression Tree创建对象性能的比较(终结版)》一文,请没有看过该博文的朋友先前往围观,或许会得到一些启发。而我想知道如果创建对象带参数会是什么样的情况呢,可惜没有看到,于是自己动手试试,作为前文的一点补充。而本文另一个目的是,对于多参数的情况,本人提供的实现方法实在是不雅,请高手指点如何重构它们成为一个通用的版本。Expression Tree 创建对象
1. 无参数的情况public static Func<object> CreateInstanceDelegate(this Type type) { NewExpression newExp = Expression.New(type); Expression<Func<object>> lambdaExp = Expression.Lambda<Func<object>>(newExp, null); Func<object> func = lambdaExp.Compile(); return func; }
(注:以上方法是扩展方法,请放到一个static class里)
测试:
Type type = typeof(Bar); var createInstance = type.CreateInstanceDelegate(); for (int i = 0; i < 1000000; i++) { createInstance(); }
好的,如果你看明白了上面的代码,那么接着看下去才有意义。
2. 多参数情况
我试图一步到位创建以下通用的含多参数的创建对象方法:
Func<object[], object> CreateInstanceDelegate(Type type, params object[] args)
结果多次修改尝试仍然失败,虽然我知道失败的原因,但不知道如何修正它。
//以下代码有bug! public static Func<object[], object> CreateInstanceDelegate(this Type type, params object[] args) { var construtor = type.GetConstructor(args.Select(c => c.GetType()).ToArray()); var param = buildParameters(args); NewExpression newExp = Expression.New(construtor, param); Expression<Func<object[], object>> lambdaExp = Expression.Lambda<Func<object[], object>>(newExp, param); Func<object[], object> func = lambdaExp.Compile(); return func; } static ParameterExpression[] buildParameters(object[] args) { int i = 0; List<ParameterExpression> list = new List<ParameterExpression>(); foreach (object arg in args) { list.Add(Expression.Parameter(arg.GetType(), "arg" + (i++))); } return list.ToArray(); }
(注:再次说明以上代码有bug,不能使用)
以上方法中的Lambda表达式“Expression<Func<object[], object>> ”已经定义参数是object[], 而构造函数的参数却不能自动转化。当使用以下代码作测试,
var createInstance = typeof(Bar).CreateInstanceDelegate(0, "");
结果报“Incorrect number of parameters supplied for lambda declaration” 错误。或许需要作一个表达式Convertor 就可以,不过暂时没进一步探究。
无奈之下,我唯有先搞定一个参数的情况,
public static Func<T, object> CreateInstanceDelegate<T>(this Type type) { Type paramType = typeof(T); var construtor = type.GetConstructor(new Type[] { paramType }); var param = new ParameterExpression[] { Expression.Parameter(paramType, "arg") }; NewExpression newExp = Expression.New(construtor, param); Expression<Func<T, object>> lambdaExp = Expression.Lambda<Func<T, object>>(newExp, param); Func<T, object> func = lambdaExp.Compile(); return func; }
通过使用泛型,创建Lambda表达式时不会报参数不匹配的错误了。
这里定义一个Bar类型作测试用,有3个构造函数:
public class Bar { public Bar() { } public Bar(int num) { } public Bar(int num, string str) { } }
测试例子:
Type type = typeof(Bar); var createInstance = type.CreateInstanceDelegate<int>(); for (int i = 0; i < count; i++) { createInstance(i); }
这里列出 Activator.CreateInstance,Expression Tree 和直接使用 new 的性能测试代码:
static void Test2() { Console.WriteLine("Test2 - CreateInstance(带1参数): "); Stopwatch watcher = new Stopwatch(); Type type = typeof(Bar); watcher.Reset(); watcher.Start(); for (int i = 0; i < count; i++) { Activator.CreateInstance(type, i); } watcher.Stop(); Console.WriteLine("Activator: " + watcher.Elapsed); watcher.Reset(); watcher.Start(); var createInstance = type.CreateInstanceDelegate<int>(); for (int i = 0; i < count; i++) { createInstance(i); } watcher.Stop(); Console.WriteLine("Expression: " + watcher.Elapsed); watcher.Reset(); watcher.Start(); for (int i = 0; i < count; i++) { new Bar(i); } watcher.Stop(); Console.WriteLine("Direct: " + watcher.Elapsed); Console.WriteLine(); }
测试通过,而且性能也优于Activator.CreateInstance,详细结果请看文章末的总体测试结果。
问题总结:
实际开发中,我希望得到的版本是跟Activator.CreateInstance 一样的:public static object CreateInstance(Type type, params object[] args);
那么,得到问题一:如何来封装以下方法
Func<T, object> CreateInstanceDelegate<T>(this Type type)
如何缓存这种委托 Func<T, object> ?
使用Expression Tree创建含两个参数的构造函数,虽然也是照葫芦画瓢的事情,如
public static Func<T1, T2, object> CreateInstanceDelegate<T1, T2>(this Type type) { var types = new Type[] { typeof(T1), typeof(T2) }; var construtor = type.GetConstructor(types); int i = 0; var param = types.Select(t => Expression.Parameter(t, "arg" + (i++))).ToArray(); NewExpression newExp = Expression.New(construtor, param); Expression<Func<T1, T2, object>> lambdaExp = Expression.Lambda<Func<T1, T2, object>>(newExp, param); Func<T1, T2, object> func = lambdaExp.Compile(); return func; }
但是这是个好方法吗?我想不是,那么得到问题二: 对于任意多个参数应该怎么写才合理?
代码下载:CoolCode.Linq.Expressions.rar
总体测试结果:
测试次数皆是一百万次Test1 - CreateInstance(无参数): Activator: 00:00:00.1673892 Expression: 00:00:00.0126696 Direct: 00:00:00.0067769 Test2 - CreateInstance(带1参数): Activator: 00:00:02.9516177 Expression: 00:00:00.0135498 Direct: 00:00:00.0074452 Test3 - CreateInstance(带2参数): Activator: 00:00:03.1932820 Expression: 00:00:00.0151513 Direct: 00:00:00.0063228
以上结果显示 Activator.CreateInstance 如果带多个参数,速度明显下降,而后两者则影响不大。
相关文章推荐
- Activator.CreateInstance 方法创建对象和Expression Tree创建对象性能的比较(构造函数含多参数的情况)
- 再谈Activator.CreateInstance(Type type)方法创建对象和Expression Tree创建对象性能的比较(更新版)
- 探究.net对象的创建,质疑《再谈Activator.CreateInstance(Type type)方法创建对象和Expression Tree创建对象性能的比较》
- Activator.CreateInstance(Type type)方法创建对象和Expression Tree创建对象性能的比较(终结版)
- 构造函数带有参数的情况.反射.用Activator.CreateInstance代替new实现类的实例化
- Expression Tree创建任意构造函数参数的方法
- JavaScript中创建对象的方法:工厂模式,构造函数模式, 原型模式
- ASP创建对象的两种方法比较
- 意外发现在调用Activator.CreateInstance的时候在构造函数处加断点居然可以~~
- 什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder对象的append方法连接字符串性能更好?
- 构造函数创建对象和Object.create()实现继承
- Expression Tree 创建任意多个参数的构造函数Lambda表达式
- JAVA之File类创建对象构造函数传参数需要注意的几点
- Activator.CreateInstance 方法 (Type) 的用法
- C# Activator.CreateInstance()方法
- C# Activator.CreateInstance()方法使用
- JS中定义对象方式二: 使用工厂方式创建对象(传递参数,及其改进方法)
- 1.import和include区别 2.NSLog 和printf区别 3.创建对象做的事情 4. 类和对象方法比较 5 匿名对象优缺点 6. 封装 7.作用域范围 8.id和instancetype 9.自定义构造方法规范 10.nil和Nil及NULL、NSNull区别
- 关于Assembly.CreateInstance()与Activator.CreateInstance()方法
- ReflectionClass::newInstance 在调用构造函数中含有引用参数的时候报错的解决方法