CodeProjectのDynamicBuilderと同様の方法で、リフレクションを使用せずにオブジェクトのプロパティを設定したいと思います。CodeProjectの例は、DataReaderまたはDataRecordを使用してエンティティにデータを入力するように調整されています。私はこれをいくつかのDALで効果的に使用しています。次に、辞書またはその他のデータに依存しないオブジェクトを使用するように変更して、DAL以外のコード(現在リフレクションを使用している場所)で使用できるようにします。私はOpCodesとILについてほとんど何も知りません。私はそれがうまく機能し、反射よりも速いことを知っています。
私はCodeProjectの例を変更しようとしましたが、ILを知らなかったため、2行で行き詰まりました。
- それらの1つはdbnullsを扱っており、それを失う可能性があると確信していますが、その前後の行が関連しているかどうか、またどちらを実行する必要があるかはわかりません。
- もう1つは、以前はデータレコードから値を引き出していたもので、現在はディクショナリから値を引き出す必要があると思います。「getValueMethod」を「property.Value」に置き換えることができると思いますが、よくわかりません。
私はこの猫の皮を剥ぐための代替/より良い方法にもオープンです。
これまでのコードは次のとおりです(コメントアウトされた行は私が固執している行です):
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を使用)を使用すると、コードが100倍単純化されます。私は今、次のテストを行っています:
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()とPropertyDescriptor.SetValue()の両方のパフォーマンスに関する考慮事項に関するコメントは大歓迎です...