动态对象属性填充器(无反射)
我想在不使用反射的情况下填充对象的属性,其方式类似于 CodeProject 上的 DynamicBuilder< /a>. CodeProject 示例专为使用 DataReader 或 DataRecord 填充实体而定制。 我在几个 DAL 中使用了这个,效果很好。 现在我想修改它以使用字典或其他与数据无关的对象,以便我可以在非 DAL 代码中使用它——我当前使用反射的地方。 我对操作码和 IL 几乎一无所知。 我只知道它效果很好并且比反射更快。
我尝试修改 CodeProject 示例,但由于我对 IL 的无知,我陷入了两行。
- 其中之一处理 dbnulls,我很确定我可能会丢失它,但我不知道它前面和后面的行是否相关,以及其中哪一行也需要删除。
- 我认为另一个是之前从数据记录中提取值的,现在需要将其从字典中提取出来。 我想我可以用我的“property.Value”替换“getValueMethod”,但我不确定。
我也愿意接受其他/更好的剥皮这只猫的方法。
这是到目前为止的代码(注释掉的行是我所坚持的行):
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
public class Populator<T>
{
private delegate T Load(Dictionary<string, object> properties);
private Load _handler;
private Populator() { }
public T Build(Dictionary<string, object> properties)
{
return _handler(properties);
}
public static Populator<T> CreateBuilder(Dictionary<string, object> properties)
{
//private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new [] { typeof(int) });
//private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new [] { typeof(int) });
Populator<T> dynamicBuilder = new Populator<T>();
DynamicMethod method = new DynamicMethod("Create", typeof(T), new[] { typeof(Dictionary<string, object>) }, typeof(T), true);
ILGenerator generator = method.GetILGenerator();
LocalBuilder result = generator.DeclareLocal(typeof(T));
generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Stloc, result);
int i = 0;
foreach (var property in properties)
{
PropertyInfo propertyInfo = typeof(T).GetProperty(property.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy | BindingFlags.Default);
Label endIfLabel = generator.DefineLabel();
if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
{
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
//generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
//generator.Emit(OpCodes.Callvirt, getValueMethod);
generator.Emit(OpCodes.Unbox_Any, property.Value.GetType());
generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
generator.MarkLabel(endIfLabel);
}
i++;
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
dynamicBuilder._handler = (Load)method.CreateDelegate(typeof(Load));
return dynamicBuilder;
}
}
编辑:
使用 Marc Gravell 的 PropertyDescriptor 实现(使用 HyperDescriptor),代码被简化了一百倍。 我现在有以下测试:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Hyper.ComponentModel;
namespace Test
{
class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
HyperTypeDescriptionProvider.Add(typeof(Person));
var properties = new Dictionary<string, object> { { "Id", 10 }, { "Name", "Fred Flintstone" } };
Person person = new Person();
DynamicUpdate(person, properties);
Console.WriteLine("Id: {0}; Name: {1}", person.Id, person.Name);
Console.ReadKey();
}
public static void DynamicUpdate<T>(T entity, Dictionary<string, object> properties)
{
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T)))
if (properties.ContainsKey(propertyDescriptor.Name))
propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]);
}
}
}
关于 TypeDescriptor.GetProperties() 和 TypeDescriptor.GetProperties() 的性能注意事项的任何评论 欢迎 PropertyDescriptor.SetValue()...
I want to populate an object's properties without using reflection in a manner similar to the DynamicBuilder on CodeProject. The CodeProject example is tailored for populating entities using a DataReader or DataRecord. I use this in several DALs to good effect. Now I want to modify it to use a dictionary or other data agnostic object so that I can use it in non DAL code --places I currently use reflection. I know almost nothing about OpCodes and IL. I just know that it works well and is faster than reflection.
I have tried to modify the CodeProject example and because of my ignorance with IL, I have gotten stuck on two lines.
- One of them deals with dbnulls and I'm pretty sure I can just lose it, but I don't know if the lines preceding and following it are related and which of them will also need to go.
- The other, I think, is the one that pulled the value out of the datarecord before and now needs to pull it out of the dictionary. I think I can replace the "getValueMethod" with my "property.Value" but I'm not sure.
I'm open to alternative/better ways of skinning this cat too.
Here's the code so far (the commented out lines are the ones I'm stuck on):
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
public class Populator<T>
{
private delegate T Load(Dictionary<string, object> properties);
private Load _handler;
private Populator() { }
public T Build(Dictionary<string, object> properties)
{
return _handler(properties);
}
public static Populator<T> CreateBuilder(Dictionary<string, object> properties)
{
//private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new [] { typeof(int) });
//private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new [] { typeof(int) });
Populator<T> dynamicBuilder = new Populator<T>();
DynamicMethod method = new DynamicMethod("Create", typeof(T), new[] { typeof(Dictionary<string, object>) }, typeof(T), true);
ILGenerator generator = method.GetILGenerator();
LocalBuilder result = generator.DeclareLocal(typeof(T));
generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Stloc, result);
int i = 0;
foreach (var property in properties)
{
PropertyInfo propertyInfo = typeof(T).GetProperty(property.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy | BindingFlags.Default);
Label endIfLabel = generator.DefineLabel();
if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
{
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
//generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
//generator.Emit(OpCodes.Callvirt, getValueMethod);
generator.Emit(OpCodes.Unbox_Any, property.Value.GetType());
generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
generator.MarkLabel(endIfLabel);
}
i++;
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
dynamicBuilder._handler = (Load)method.CreateDelegate(typeof(Load));
return dynamicBuilder;
}
}
EDIT:
Using Marc Gravell's PropertyDescriptor implementation (with HyperDescriptor) the code is simplified a hundred-fold. I now have the following test:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Hyper.ComponentModel;
namespace Test
{
class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
HyperTypeDescriptionProvider.Add(typeof(Person));
var properties = new Dictionary<string, object> { { "Id", 10 }, { "Name", "Fred Flintstone" } };
Person person = new Person();
DynamicUpdate(person, properties);
Console.WriteLine("Id: {0}; Name: {1}", person.Id, person.Name);
Console.ReadKey();
}
public static void DynamicUpdate<T>(T entity, Dictionary<string, object> properties)
{
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T)))
if (properties.ContainsKey(propertyDescriptor.Name))
propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]);
}
}
}
Any comments on performance considerations for both TypeDescriptor.GetProperties() & PropertyDescriptor.SetValue() are welcome...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编辑:所有这些基本上都是 dapper 所做的 - 但 dapper 更加优化。 如果我今天写这个答案,它会简单地写成:“使用 dapper”。
如果您对 IL 不太“了解”,还有一些替代方案可以让您获得 IL 的速度和方便的反思。
第一个示例:
HyperDescriptor - 使用自定义
PropertyDescriptor
为您处理 IL 的模型,因此您拥有的只是这样的代码(加上用于启用 HyperDescriptor 的单行代码):第二个示例:
LINQ 表达式 - 相当冗长,但我已经讨论过这一点(事实证明,上述)在 usenet 上 - 请参阅
Edit: all of this is basically what dapper does - but dapper is much more optimized. If I was writing this answer today, it would read simply: "use dapper".
If you aren't hugely "up" on IL, there are alternatives that get you the speed of IL and the convenience of reflection.
First example:
HyperDescriptor - uses a custom
PropertyDescriptor
model that deals with the IL for you, so all you have is code like (plus the one-liner to enableHyperDescriptor
):Second example:
LINQ expressions - quite lengthy, but I've discussed this (and the above, it turns out) on usenet - see this archive.
是的,你可以使用这样的代码:
......
Yeah,you can use code like this:
.... ...