玩转动态编译 - 高级篇:三,实例属性的读取与设置

  • 实例属性的读取

先来回顾下静态属性读取的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 

} 
玩转动态编译 - 高级篇:三,实例属性的读取与设置
string AAA()

{

    return MyClass.Name;

}
C#代码

再来看下读取实例属性的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 

}
玩转动态编译 - 高级篇:三,实例属性的读取与设置
string AAA(MyClass my)

{

    return my.Name;

}
C#代码

区别很明显,多个一个指令ldarg.0 ,并且指令有所区别

//     将索引为 0 的参数加载到计算堆栈上。

public static readonly OpCode 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;

}

 把他们整合起来

玩转动态编译 - 高级篇:三,实例属性的读取与设置
public class ObjectProperty

{

    /// <summary> 属性信息

    /// </summary>

    public PropertyInfo Info { get; set; }

    /// <summary> Set方法委托

    /// </summary>

    public PropertySetter Setter { get; set; }

    //缓存

    static readonly Dictionary<Type, ObjectProperty[]> Cache = new Dictionary<Type, ObjectProperty[]>();



    /// <summary> 获取一个类中的所有公开实例属性和它们的Set方法委托

    /// </summary>

    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;

    }

    /// <summary> 创建指定属性的Set方法委托

    /// </summary>

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

    }

}
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和一个实体类来测试下

玩转动态编译 - 高级篇:三,实例属性的读取与设置
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#代码

 

玩转动态编译 - 高级篇:三,实例属性的读取与设置

  • 反射和IL

就功能上来说IL可以做的,反射都可以做.基本上IL的操作指令很多参数都是需要用到反射对象的

那么我们为什么要选择麻烦的IL,而不直接用反射呢,答案就是性能

就拿上面的栗子来说,如果我们用反射来实现的话是这样的

玩转动态编译 - 高级篇:三,实例属性的读取与设置
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;

}
反射代码

 

玩转动态编译 - 高级篇:三,实例属性的读取与设置

这只是构造10000个只有5个属性的实体类而已

因为先运行的是动态编译IL的测试,所以缓存什么的都已经在这个时候建好了,下面反射只是调用缓存

如果这个测试还看不出太大区别的话,那就看下直接对比Set部分的性能

玩转动态编译 - 高级篇:三,实例属性的读取与设置

一般来说是6倍左右的性能差异

 

你可能感兴趣的:(动态编译)