动态对象属性populator(无reflection)

我想在不使用reflection的情况下填充对象的属性,其方式类似于CodeProject上的DynamicBuilder 。 CodeProject示例是为使用DataReader或DataRecord填充实体而定制的。 我在几个DAL中使用它以达到很好的效果。 现在我想修改它以使用字典或其他数据无关的对象,以便我可以在非DAL代码中使用它 – 我目前使用reflection。 我对OpCodes和IL几乎一无所知。 我只知道它运作良好,比reflection更快。

我试图修改CodeProject示例,由于我对IL的无知,我已经陷入两行。

  • 其中一个处理dbnulls,我很确定我可以丢失它,但我不知道它之前和之后的行是否相关以及它们中的哪一个也需要去。
  • 另一方面,我认为,是之前从数据线中取出价值的,现在需要将其从字典中拉出来。 我想我可以用“property.Value”替换“getValueMethod”,但我不确定。

我也愿意采用替代/更好的方法来剥皮这只猫。

这是迄今为止的代码(注释掉的行是我坚持的):

using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; public class Populator { private delegate T Load(Dictionary properties); private Load _handler; private Populator() { } public T Build(Dictionary properties) { return _handler(properties); } public static Populator CreateBuilder(Dictionary 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 dynamicBuilder = new Populator(); DynamicMethod method = new DynamicMethod("Create", typeof(T), new[] { typeof(Dictionary) }, 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 { { "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 entity, Dictionary properties) { foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T))) if (properties.ContainsKey(propertyDescriptor.Name)) propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]); } } } 

欢迎任何有关TypeDescriptor.GetProperties()和PropertyDescriptor.SetValue()的性能注意事项的评论……

编辑:所有这些基本上都是dapper所做的 – 但是dapper更加优化。 如果我今天写这个答案,那就简单地说:“用dapper”。


如果你对IL没有太大的“兴趣”,那么有一些替代方案可以让你获得IL的速度和reflection的便利性。

第一个例子:

HyperDescriptor – 使用一个自定义的PropertyDescriptor模型来处理IL,所以你只需要代码(加上一行启用HyperDescriptor ):

 public static IEnumerable Read(IDataReader reader) where T : class, new() { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T)); PropertyDescriptor[] propArray = new PropertyDescriptor[reader.FieldCount]; for (int i = 0; i < propArray.Length; i++) { propArray[i] = props[reader.GetName(i)]; } while(reader.Read()) { T item = new T(); for (int i = 0; i < propArray.Length; i++) { object value = reader.IsDBNull(i) ? null : reader[i]; propArray[i].SetValue(item, value); } yield return item; } } 

第二个例子:

LINQ表达式 - 相当冗长,但我在usenet上讨论了这个(以及上面的结果) - 请参阅此存档 。

是的,你可以使用这样的代码:

  for (int i = 0; i < dataRecord.FieldCount; i++) { PropertyInfo propertyInfo = t.GetProperty(dataRecord.GetName(i)); LocalBuilder il_P = generator.DeclareLocal(typeof(PropertyInfo)); Label endIfLabel = generator.DefineLabel(); 

...... ......