我的ORM一直是用反射来实现动态生成Entity的,最近想提高一下效率,就尝试了一下用其他的方法来生成实体类。平时看到的资料都说用Expression的速度已经接近于Emit了,效率非常高,但测试的结果却让我大跌眼镜。。。下面对直接赋值、反射、委托、Expression和Emit 动态方法五种方式来生成实体类进行测试。如果大家有其他更好的方法来生成实体类,请不吝赐教,谢谢。
----------------
Update1: 重新修改了一下测试用例,让测试更加合理化,原来的循环是不合理的,现在改为一个DataTable ,生成一个List<T> 的实体类List
Update2: 增加了Emit 的方法,DynamicMethod确实比较快。重写了Expression的测试方法,缓存了Func<>代理后,速度确实快多了。但Delegate的方法没想到怎么能不每次都CreateDelegate,谁有好办法可以讲一下,谢谢先。
----------------
先上测试结果:(环境:Windows 7 64 bit, I7 950,12G ram, VS2010)
Assign ,generate entity, total 10000 rows, time: 3
Emit ,generate entity, total 10000 rows, time: 7
Expression ,generate entity, total 10000 rows, time: 38
Reflection ,generate entity, total 10000 rows, time: 39
Delegate ,generate entity, total 10000 rows, time: 137
Press any key to continue...
从结果中可以,直接赋值最快(但写和维护太麻烦,实际项目中一般不考虑),然后就是Emit,IL确实比较快。Expression比反射快,但还是比Emit差不少,是不是测试用例还是不合理呢?
详细说明一下我的测试过程:
1、原始数据是一个DataTable, 一万行数据,三列。分别为Name,Age,Sex ,下面所有的转化是把整个DataTable转为一个List<T>
public static DataTable GetCustomer(int _rows)
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("Name", typeof(System.String)));
dt.Columns.Add(new DataColumn("Age", typeof(System.String)));
dt.Columns.Add(new DataColumn("Sex", typeof(System.String)));
for (int i = 1; i <= _rows; i++)
{
DataRow dr = dt.NewRow();
dr["Name"] = "Andy" + i.ToString();
dr["Age"] = i.ToString();
dr["Sex"] = "Male" + i.ToString();
dt.Rows.Add(dr);
}
return dt;
}
2、要生成的实体类也很简单,只有三个property,为了避免类型转换等问题,用最简单的,都是string 类型。
public class Customer
{
public string Name { get; set; }
public string Age { get; set; }
public string Sex { get; set; }
}
3、直接赋值的方法:
public List<Customer> GetEntity(DataTable dt)
{
List<Customer> lst=new List<Customer>();
foreach (DataRow dr in dt.Rows)
{
Customer cus = new Customer();
cus.Name = dr["Name"].ToString();
cus.Age = dr["Age"].ToString();
cus.Sex = dr["Sex"].ToString();
lst.Add(cus);
}
return lst;
}
4、反射生成实体类的方法:
1 public class ToEntityByReflection<T>
2 {
3 public List<T> GetEntity(DataTable dt)
4 {
5 List<T> lst = new List<T>();
6 Type type = typeof(T);
7
8 foreach (DataRow dr in dt.Rows)
9 {
10 T model = (T)Activator.CreateInstance(typeof(T));
11 foreach (PropertyInfo pi in type.GetProperties())
12 {
13 pi.SetValue(model, Convert.ChangeType(dr[pi.Name], pi.PropertyType), null);
14 }
15 lst.Add(model);
16 }
17 return lst;
18 }
19 }
5、Delegate 生成实体类的方法:
1 public delegate void SetString(string value);
2 public class ToEntityByDelegate<T>
3 {
4 public List<T> GetEntity(DataTable dt)
5 {
6 List<T> lst = new List<T>();
7 Type type = typeof(T);
8 SetString setDelegateString;
9
10 foreach (DataRow dr in dt.Rows)
11 {
12 T model = (T)Activator.CreateInstance(typeof(T));
13 foreach (DataColumn dc in dt.Columns)
14 {
15 setDelegateString = CreateStringDelegate(model, dc.ColumnName); // 谁知道怎么缓存这个???
16 setDelegateString(dr[dc.ColumnName].ToString());
17 }
18 lst.Add(model);
19 }
20
21 return lst;
22 }
23
24 private static SetString CreateStringDelegate(object obj, string PropertyName)
25 {
26 MethodInfo mi = obj.GetType().GetProperty(PropertyName).GetSetMethod();
27 Type type = typeof(SetString);
28 return (SetString)Delegate.CreateDelegate(type, obj, mi);
29 }
30 }
6、Expression 生成实体的方法:
1 public static class ToEntityByExpression
2 {
3 public static List<T> GetEntity<T>(DataTable dt)
4 {
5 List<T> lst = new List<T>();
6 Dictionary<string, Func<T, object, object>> dic = new Dictionary<string, Func<T, object, object>>();
7 foreach (DataColumn dc in dt.Columns)
8 {
9 PropertyInfo pi = typeof(T).GetProperty(dc.ColumnName);
10 if (!dic.ContainsKey(dc.ColumnName))
11 {
12 Func<T, object, object> fc = SetDelegate<T>(pi.GetSetMethod(), pi.PropertyType);
13 dic.Add(dc.ColumnName, fc);
14 }
15 }
16
17 foreach (DataRow dr in dt.Rows)
18 {
19 T model = (T)Activator.CreateInstance(typeof(T));
20
21 foreach (DataColumn dc in dt.Columns)
22 {
23 Func<T, object, object> fc = dic[dc.ColumnName];
24 fc(model, dr[dc.ColumnName]);
25 }
26 lst.Add(model);
27 }
28
29 return lst;
30 }
31
32
33
34 static Func<T, object,object> SetDelegate<T>(MethodInfo m, Type type)
35 {
36 ParameterExpression param_obj = Expression.Parameter(typeof(T), "obj");
37 ParameterExpression param_val = Expression.Parameter(typeof(object), "val");
38 UnaryExpression body_val = Expression.Convert(param_val, type);
39 MethodCallExpression body = Expression.Call(param_obj, m, body_val);
40 Action<T, object> set = Expression.Lambda<Action<T, object>>(body, param_obj, param_val).Compile();
41
42 return (instance, v) =>
43 {
44 set(instance, v);
45 return null;
46 };
47 }
48 }
7、Emit 动态方法:
1 public static class ToEntityByEmit
2 {
3 public static List<T> GetList<T>(DataTable dt)
4 {
5 List<T> lst = new List<T>();
6 if (dt == null) return lst;
7 DataTableEntityBuilder<T> eblist = DataTableEntityBuilder<T>.CreateBuilder(dt.Rows[0]);
8 foreach (DataRow dr in dt.Rows)
9 {
10 lst.Add(eblist.Build(dr));
11 }
12 dt.Dispose();
13 dt = null;
14 return lst;
15 }
16 }
17
18 public class DataTableEntityBuilder<T>
19 {
20 private static readonly MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) });
21 private static readonly MethodInfo isDBNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });
22 private delegate T Load(DataRow dr);
23
24 private Load handler;
25 private DataTableEntityBuilder() { }
26
27 public T Build(DataRow dr)
28 {
29 return handler(dr);
30 }
31
32 public static DataTableEntityBuilder<T> CreateBuilder(DataRow dr)
33 {
34 DataTableEntityBuilder<T> dynamicBuilder = new DataTableEntityBuilder<T>();
35 DynamicMethod method = new DynamicMethod("DynamicCreateEntity", typeof(T), new Type[] { typeof(DataRow) }, typeof(T), true);
36 ILGenerator generator = method.GetILGenerator();
37 LocalBuilder result = generator.DeclareLocal(typeof(T));
38 generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
39 generator.Emit(OpCodes.Stloc, result);
40
41 for (int i = 0; i < dr.ItemArray.Length; i++)
42 {
43 PropertyInfo pi = typeof(T).GetProperty(dr.Table.Columns[i].ColumnName);
44 Label endIfLabel = generator.DefineLabel();
45 if (pi != null && pi.GetSetMethod() != null)
46 {
47 generator.Emit(OpCodes.Ldarg_0);
48 generator.Emit(OpCodes.Ldc_I4, i);
49 generator.Emit(OpCodes.Callvirt, isDBNullMethod);
50 generator.Emit(OpCodes.Brtrue, endIfLabel);
51 generator.Emit(OpCodes.Ldloc, result);
52 generator.Emit(OpCodes.Ldarg_0);
53 generator.Emit(OpCodes.Ldc_I4, i);
54 generator.Emit(OpCodes.Callvirt, getValueMethod);
55 generator.Emit(OpCodes.Unbox_Any, pi.PropertyType);
56 generator.Emit(OpCodes.Callvirt, pi.GetSetMethod());
57 generator.MarkLabel(endIfLabel);
58 }
59 }
60 generator.Emit(OpCodes.Ldloc, result);
61 generator.Emit(OpCodes.Ret);
62 dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
63 return dynamicBuilder;
64 }
65 }
8、主程序:
class Program
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Stopwatch sw = new Stopwatch();
6 int rows = 10000;
7 DataTable dt = DataTableData.GetCustomer(rows);
8
9 sw.Restart();
10 List<Customer> cusAssign = new ToEntityByAssign().GetEntity(dt);
11 sw.Stop();
12 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Assign".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
13 //Console.WriteLine("The fifth element is: {0}",cusAssign[4].Name+" , "+ cusAssign[4].Age+" , "+cusAssign[4].Sex);
14
15 sw.Restart();
16 List<Customer> cusEmit = ToEntityByEmit.GetList<Customer>(dt);
17 sw.Stop();
18 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Emit".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
19 //Console.WriteLine("The fifth element is: {0}", cusEmit[4].Name + " , " + cusEmit[4].Age + " , " + cusEmit[4].Sex);
20
21 sw.Restart();
22 List<Customer> cusExpress = ToEntityByExpression.GetEntity<Customer>(dt);
23 sw.Stop();
24 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Expression".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
25 //Console.WriteLine("The fifth element is: {0}", cusExpress[4].Name + " , " + cusExpress[4].Age + " , " + cusExpress[4].Sex);
26
27 sw.Restart();
28 List<Customer> cusReflection = new ToEntityByReflection<Customer>().GetEntity(dt);
29 sw.Stop();
30 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Reflection".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
31 //Console.WriteLine("The fifth element is: {0}", cusReflection[4].Name + " , " + cusReflection[4].Age + " , " + cusReflection[4].Sex);
32
33 sw.Restart();
34 List<Customer> cusDelegate = new ToEntityByDelegate<Customer>().GetEntity(dt);
35 sw.Stop();
36 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Delegate".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
37 //Console.WriteLine("The fifth element is: {0}", cusDelegate[4].Name + " , " + cusDelegate[4].Age + " , " + cusDelegate[4].Sex);
38
39
40 Console.WriteLine();
41 Console.WriteLine("Press any key to continue...");
42 Console.ReadKey();
43 }
44 }
这只一个用最简单的实例来测试,因个人水平有限,测试做的简陋,如果有不当的地方,请大家指出。
非常感谢回帖的各位同学,让我学到很多东西。
学习的过程中参考了Artech 和老赵等人的文章如下:
http://www.cnblogs.com/artech/archive/2011/03/26/1996057.html
http://www.cnblogs.com/JeffreyZhao/archive/2008/11/24/invoke-method-by-lambda-expression.html
http://hi.baidu.com/ysuhy/blog/item/5b993f003168ca0d728da540.html
完整测试代码下载:
Ver 3: http://files.cnblogs.com/andyliu/testGenerateEntity.rar
以前项目中使用的单例一直是用的最简单的C#写法,是Thread Safe的,多线程下只会有一个实例。代码如下:
public sealed class Singleton
{
public static readonly Singleton Instance = new Singleton();
private Singleton() { }
}
最近项目有了新的需求,需要多个线程中实现多个实例,最好是在每个线程中是单例的。于是修改代码为最基本的实现,这种最基本的实现方法在网上几乎所有的中英文资料都是众口一词的说这种方法在多个线程下会开多个实例,但结果。。。无论如何,永远是单实例,百思不得其解,代码如下,请大家指正。测试环境为Windows 7 64bit, VS2010, .net framework 4.0
Class Singleton:
1 public class Singleton : ICloneable
2 {
5 private static Singleton instance;
6 public static Singleton Instance
7 {
8 get
9 {
10 if (instance == null)
11 instance = new Singleton();
12 return instance;
13
14 }
15 }
16
17 private string instanceID;
18
19 private Singleton()
20 {
21 // 随机返回一个ID,来证明此实例是否为同一个。
22 instanceID = new Random().Next().ToString();
23 }
24
25 public void GetInstanceID()
26 {
27 System.Threading.Thread.Sleep(3000);
28 MessageBox.Show("Instance ID: " + instanceID);
29 }
30
31 public object Clone()
32 {
33 return new Singleton();
34 }
35 }
ICloneable 是为了多个线程中能实现多实例而实现的,但依然不行。开始时是没有实现ICloneable接口的,但也不行。
Form1中的调用代码 :
1 private void button1_Click(object sender, EventArgs e)
2 {
3 Singleton.Instance.GetInstanceID();
4 new System.Threading.Tasks.TaskFactory().StartNew(() =>
5 {
6 Singleton.Instance.GetInstanceID();
7 });
8
9 }
结果,在当前线程下得到IstanceID之后,多线程里的InstanceID 始终是同一个,证明在其他线程中并没有产生新的实例,是我的实现方法有问题,还是什么原因呢?请大家指正。
运行结果如下:(使用backgroudnworker等方法也测试过,也是始终是同一个实例)

建立一个Attribute:
public class EntityMappingAttribute : Attribute
{
public string TableName{get;set;}
public string SortOrder { get; set; }
}
建一个类:
public class User
{
[EntityMapping(TableName = "test",SortOrder="5")]
public string name { get; set; }
}
得到attribute的值 :
User u = new User();
u.name = "Andy";
PropertyInfo pi = u.GetType().GetProperty("name");
var lst = pi.GetCustomAttributes(true);
foreach (var l in lst)
{
EntityMappingAttribute ema = l as EntityMappingAttribute;
richTextBox1.Text += ema.TableName + "\n";
richTextBox1.Text += ema.SortOrder + "\n";
}
最近有一个客户有个比较怪的要求,网站中有一个页面,他想让用户不能加入到收藏夹中。我想了几种办法,都不理想,谁有好的方法吗?
按照客户的要求,当想把那个网页在IE里加入收藏夹时,最好点击IE上的菜单,选择加入收藏之后加的是我们指定的一个页面,而不是当前页。按照这个思路去做,没找到方法,没有成功。
第二个思路:点击加入收藏,出错,不能加入收藏。也没成功。
第三个思路:用Frame,不管怎么点,地址栏里的链接都是一个首页的链接,这样不管客户在哪里选择加入收藏,收藏夹里加入的都是首页的链接。实践下来,比如目前的地址是 www.abc.com/abc.htm ,而地址栏上显示的是 www.abc.com ,这时点击IE菜单上的加入收藏,加入到收藏夹里的确实是 www.abc.com 而不是www.abc.com/abc.htm ,但IE的智能确在此时做了坏事情,IE居然会自动记忆当前Frame中调用的实际地址,虽然当前加入收藏夹的地址是www.abc.com ,但当从收藏夹里选择时,打的网页还是Frame里的abc.htm 而不是Frame里默认的首页 index.htm .
到现在还没想到什么好的办法来解决,如果谁以前遇到过类似的问题,或者对收藏夹的原理比较熟悉,给一点儿指点,先谢谢了。
这段时间忙着搬新公司,一直也没上来写东西,今天在面试简历的看到一个非常有趣的自我介绍,原文如下:
自我评价:本人安于工作,诚实肯干,勇于挑战和接受新知识(现大专在读),相信我的努力定能为你带来一分意外的收获。相信他的能力吧(擅长骗取弱小可欺的女孩的身体及钱财,弱女子到手后要求她买笔记本电脑,包括在两小孩出生时的负担同时在北大青鸟学费3万多和伙食的一切费用都是若女子一人负担,现在毕业了却弃妻抛子。是问这样的人才你们能接受。)
开始第一遍没看懂,什么乱七八糟的,难不成在标榜自己会欺骗弱女子??又看了一遍,大概猜到了原由,想必是他和老婆之间的感情出了问题,老婆知道他的密码,然后上到人力网上改了他的自我介绍,呵呵。他自己还不知道啊,还把简历投到我公司来。。。。。。。
这个社会啊。。
珠海Asp.net2.0QQ群建立了,大家有空多来看看。
群号:14639777
注:仅用于技术交流使用!


