- 实例属性的读取
先来回顾下静态属性读取的IL代码:
.method public hidebysig instance string AAA() cil managed
{.maxstack 8L_0000: call string blqw.IL.Demo.Program/MyClass::get_Name()L_0005: ret
}
string AAA()
{return MyClass.Name;
}
再来看下读取实例属性的IL代码
.method private hidebysig instance string AAA(class blqw.IL.Demo.Program/MyClass my) cil managed
{.maxstack 8L_0000: ldarg.0 L_0001: callvirt instance string blqw.IL.Demo.Program/MyClass::get_Name()L_0006: ret
}
string AAA(MyClass my)
{return my.Name;
}
区别很明显,多个一个指令ldarg.0 ,并且指令有所区别
// 将索引为 0 的参数加载到计算堆栈上。
public static readonly OpCode Ldarg_0;
操作实例方法和操作静态方法不同,静态方法不需要任何额外的参数,而实例方法必须要提供一个参数,这个参数指示操作的实例对象
转换成C#代码就是这样的
public static Func
{var type = typeof(MyClass);var prop = type.GetProperty("Name");//反射属性var dm = new DynamicMethod("", typeof(string), new[] { typeof(MyClass) }, type);var il = dm.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Callvirt, prop.GetGetMethod());il.Emit(OpCodes.Ret);return (Func
}
- 实例属性的设置
IL代码
.method private hidebysig instance void AAA(class blqw.IL.Demo.Program/MyClass my, string name) cil managed
{.maxstack 8L_0000: ldarg.0 L_0001: ldarg.1 L_0002: callvirt instance void blqw.IL.Demo.Program/MyClass::set_Name(string)L_0007: ret
}
好吧,又多了一个参数,不过这个是显而易见的,既然你要设置值,总要把值当作参数传递进去吧
对应C#代码如下:
public static Action
{var type = typeof(MyClass);var prop = type.GetProperty("Name");//反射属性var dm = new DynamicMethod("", null, new[] { typeof(MyClass), typeof(string) }, type);var il = dm.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);il.Emit(OpCodes.Callvirt, prop.GetSetMethod());il.Emit(OpCodes.Ret);return (Action
}
重复来重复去都是这几样东西了..有些无聊了吧...
那么接下来就做一些实际情况下的应用了
- 实际应用
这次我要举起来的栗子就是DataTable转模型对象
不过在这之前,我要对现有的方法进行一些调整
public delegate void PropertySetter(object instance, object value);
public static PropertySetter CreateSetter(PropertyInfo property)
{var type = property.DeclaringType;var dm = new DynamicMethod("", null, new[] { typeof(object), typeof(object) }, type);//=== IL ===var il = dm.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);if (property.PropertyType.IsValueType)//判断属性类型是否是值类型
{il.Emit(OpCodes.Unbox,property.PropertyType);//如果是值类型就拆箱
}else{il.Emit(OpCodes.Castclass, property.PropertyType);//否则强转
}il.Emit(OpCodes.Callvirt, property.GetSetMethod());il.Emit(OpCodes.Ret);//=== IL ===return (PropertySetter)dm.CreateDelegate(typeof(PropertySetter));
}
修改的地方不是很多,应该不难理解,关于类型转换部分,请参考上一篇
现在我可以很方便的通过这个方法创建一个任意实例属性的Set方法委托
接下来我需要一个的新的类
public class ObjectProperty
{public PropertyInfo Info { get; set; }public PropertySetter Setter { get; set; }
}
这个类包含一个属性和这个属性的Set方法委托
在接下来我需要一个方法,把任意一个类中的所有公开的实例属性,转换成ObjectProperty集合
static readonly Dictionary<Type, ObjectProperty[]> Cache &#61; new Dictionary<Type, ObjectProperty[]>();public static ObjectProperty[] GetProperties(Type type)
{ObjectProperty[] arr;if (Cache.TryGetValue(type, out arr))//优先从缓存中获取
{return arr;}PropertyInfo[] ps &#61; type.GetProperties(); arr &#61; new ObjectProperty[ps.Length];for (int i &#61; 0; i
}
把他们整合起来
public class ObjectProperty
{///
{return arr;}PropertyInfo[] ps &#61; type.GetProperties();arr &#61; new ObjectProperty[ps.Length];for (int i &#61; 0; i
{il.Emit(OpCodes.Unbox, property.PropertyType);//如果是值类型就拆箱
}else{il.Emit(OpCodes.Castclass, property.PropertyType);//否则强转
}il.Emit(OpCodes.Callvirt, property.GetSetMethod());il.Emit(OpCodes.Ret);//&#61;&#61;&#61; IL &#61;&#61;&#61;return (PropertySetter)dm.CreateDelegate(typeof(PropertySetter));}
}
现在就可以写出一个将DataTable转为实体类的方法了
public static List
{var prop &#61; ObjectProperty.GetProperties(typeof(T));List
}
好了,我现在模拟出一个DataSet和一个实体类来测试下
public class User
{public User(){}public int Id { get; set; }public string Name { get; set; }public bool Sex { get; set; }public Guid Uid { get; set; }public DateTime Time { get; set; }public string SexText{get{return Sex ? "男" : "女";}set{Sex &#61; (value &#61;&#61; "男");}}
}
//模拟方法
static public DataSet GetDataSet(string sql)
{DataTable table &#61; new DataTable("User");table.Columns.Add("Id", typeof(int));table.Columns.Add("Name", typeof(string));table.Columns.Add("Sex", typeof(bool));table.Columns.Add("Uid", typeof(Guid));table.Columns.Add("Time", typeof(DateTime));table.Columns.Add("多出来的属性", typeof(string));for (int i &#61; 0; i <20; i&#43;&#43;){table.Rows.Add(i, "blqw" &#43; i, true, Guid.NewGuid(), DateTime.Now, "多余的");}DataSet ds &#61; new DataSet();ds.Tables.Add(table);return ds;
}
- 反射和IL
就功能上来说IL可以做的,反射都可以做.基本上IL的操作指令很多参数都是需要用到反射对象的
那么我们为什么要选择麻烦的IL,而不直接用反射呢,答案就是性能
就拿上面的栗子来说,如果我们用反射来实现的话是这样的
static public List
{var prop &#61; ObjectProperty.GetProperties(typeof(T));List
}}}list.Add(m);}return list;
}
这只是构造10000个只有5个属性的实体类而已
因为先运行的是动态编译IL的测试,所以缓存什么的都已经在这个时候建好了,下面反射只是调用缓存
如果这个测试还看不出太大区别的话,那就看下直接对比Set部分的性能
一般来说是6倍左右的性能差异