- 实例属性的读取
先来回顾下静态属性读取的IL代码:
.method public hidebysig instance string AAA() cil managed { .maxstack 8 L_0000: call string blqw.IL.Demo.Program/MyClass::get_Name() L_0005: ret }
再来看下读取实例属性的IL代码
.method private hidebysig instance string AAA(class blqw.IL.Demo.Program/MyClass my) cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: callvirt instance string blqw.IL.Demo.Program/MyClass::get_Name() L_0006: ret }
区别很明显,多个一个指令ldarg.0 ,并且指令有所区别
操作实例方法和操作静态方法不同,静态方法不需要任何额外的参数,而实例方法必须要提供一个参数,这个参数指示操作的实例对象
转换成C#代码就是这样的
public static Func<MyClass, string> ILTest() { 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<MyClass, string>)dm.CreateDelegate(typeof(Func<MyClass, string>)); }
- 实例属性的设置
IL代码
.method private hidebysig instance void AAA(class blqw.IL.Demo.Program/MyClass my, string name) cil managed { .maxstack 8 L_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<MyClass, string> ILTest() { 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<MyClass, string>)dm.CreateDelegate(typeof(Action<MyClass, string>)); }
重复来重复去都是这几样东西了..有些无聊了吧...
那么接下来就做一些实际情况下的应用了
- 实际应用
这次我要举起来的栗子就是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 = new Dictionary<Type, ObjectProperty[]>(); public static ObjectProperty[] GetProperties(Type type) { ObjectProperty[] arr; if (Cache.TryGetValue(type, out arr))//优先从缓存中获取 { return arr; } PropertyInfo[] ps = type.GetProperties(); arr = new ObjectProperty[ps.Length]; for (int i = 0; i < ps.Length; i++) { ObjectProperty op = new ObjectProperty(); op.Info = ps[i]; op.Setter = CreateSetter(op.Info); //之前定义的方法 arr[i] = op; } Cache.Add(type, arr); //加入缓存 return arr; }
把他们整合起来
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 = (value == "男");
}
}
}
//模拟方法
static public DataSet GetDataSet(string sql)
{
DataTable table = 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 = 0; i < 20; i++)
{
table.Rows.Add(i, "blqw" + i, true, Guid.NewGuid(), DateTime.Now, "多余的");
}
DataSet ds = new DataSet();
ds.Tables.Add(table);
return ds;
}
C#代码
现在就可以写出一个将DataTable转为实体类的方法了
public static List<T> ConvertToModels<T>(DataSet ds) where T : new() { var prop = ObjectProperty.GetProperties(typeof(T)); List<T> list = new List<T>(ds.Tables[0].Rows.Count); var cols = ds.Tables[0].Columns; foreach (DataRow row in ds.Tables[0].Rows) { T m = new T(); foreach (var p in prop) { if (cols.Contains(p.Info.Name)) { var val = row[p.Info.Name]; if ((val is DBNull) == false) { p.Setter(m, val); } } } list.Add(m); } return list; }
好了,我现在模拟出一个DataSet和一个实体类来测试下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
static public List<T> ConvertToModels2<T>(DataSet ds)
where T : new()
{
var prop = ObjectProperty.GetProperties(typeof(T));
List<T> list = new List<T>(ds.Tables[0].Rows.Count);
var cols = ds.Tables[0].Columns;
foreach (DataRow row in ds.Tables[0].Rows)
{
T m = new T();
foreach (var p in prop)
{
if (cols.Contains(p.Info.Name))
{
var val = row[p.Info.Name];
if (Convert.IsDBNull(val) == false)
{
p.Info.SetValue(m, val, null);//这里直接用反射的SetValue
}
}
}
list.Add(m);
}
return list;
}
反射代码
- 反射和IL
就功能上来说IL可以做的,反射都可以做.基本上IL的操作指令很多参数都是需要用到反射对象的
那么我们为什么要选择麻烦的IL,而不直接用反射呢,答案就是性能
就拿上面的栗子来说,如果我们用反射来实现的话是这样的
这只是构造10000个只有5个属性的实体类而已
因为先运行的是动态编译IL的测试,所以缓存什么的都已经在这个时候建好了,下面反射只是调用缓存
如果这个测试还看不出太大区别的话,那就看下直接对比Set部分的性能
一般来说是6倍左右的性能差异