创建用于生成 C# 字典的 lambda 的表达式树
Expression tree to create lambda for generating a c# dictionary
我有数以千计的数据流,需要转换并添加到列表中。转换通过类似于以下的反射发生
_myObservable.Subscribe(d => {
PropertyInfo[] props = d.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
var propValDict = props.ToDictionary(prop => prop.Name, prop => prop.GetValue(d, null));
myList.Add(propValDict);
});
// Datatype of d is determined during runtime and there are only 8 possibilities of the type
但是这种方法会降低性能,我认为使用反射可能是原因。我正在考虑通过其他方式提高性能。
建议似乎指向表达式树的使用,创建编译的 lambda (Func<object,Dictionary<string, object>>)
并预先将其存储在查找字典中。
//Foreach possibleType in PossibleTypes, Do below
PropertyInfo[] props = possibleType.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
var rootParam = Expression.Parameter(typeof(object), "d");
var param = Expression.Parameter(typeof(PropertyInfo), "prop");
var propertyFirst = Expression.Property(param, "Name");
var param2 = Expression.Parameter(typeof(PropertyInfo), "prop");
var callMethod = Expression.Call(param2, typeof(PropertyInfo).GetMethod(nameof(PropertyInfo.GetValue), new Type[] { typeof(object) }), rootParam);
var pro = Expression.Parameter(typeof(Array), "props");
var toDict = Expression.Invoke(pro, propertyFirst, callMethod);
var lambda = Expression.Lambda<Func<object, Dictionary<string, object>>>(toDict, rootParam);
var compiled = lambda.Compile();
我无法调用 Enumerable 的 ToDictionary class
这种方法我缺少一些东西,或者这真的会提高性能。
请帮忙...
在思考表达式时,您总是需要弄清楚等效的 C# 代码是什么样的。在这种情况下,等效的 C# 代码不会遍历 PropertyInfo
的集合,而是可能看起来像:
public static Func<object, Dictionary<string, object>> CreateConvertToPropertyDict<T>()
{
return input =>
{
var d = (T)input;
return new Dictionary<string, object>())
{
{ "Foo", d.Foo },
{ "Bar", d.Bar },
};
};
}
myList.Add(propValDict);
在表达式领域横向移动,你最终会得到类似的东西:
public static Func<object, Dictionary<string, object>> CreatePropertyDict(Type type)
{
// Consider caching these in a static field, since they're constant
var dictType = typeof(Dictionary<string, object>);
var dictCtor = dictType.GetConstructor(new[] { typeof(int) });
var dictAddMethod = dictType.GetMethod("Add");
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var blockExpressions = new List<Expression>();
// 'object input' is our input parameter
var inputParameter = Expression.Parameter(typeof(object), "input");
// MyType d;
var dVariable = Expression.Variable(type, "d");
// d = (MyType)inputObject;
blockExpressions.Add(Expression.Assign(dVariable, Expression.Convert(inputParameter, type)));
// Dictionary<string, object> dict;
var dictVariable = Expression.Variable(dictType, "dict");
// dict = new Dictionary<string, object>(3) (or however many properties there are)
blockExpressions.Add(Expression.Assign(dictVariable, Expression.New(dictCtor, Expression.Constant(properties.Length))));
foreach (var property in properties)
{
var propertyAccess = Expression.Property(dVariable, property);
// dict.Add("Foo", (object)d.Foo)
blockExpressions.Add(Expression.Call(
dictVariable,
dictAddMethod,
Expression.Constant(property.Name),
Expression.Convert(propertyAccess, typeof(object))));
};
// The final statement in a block is the return value
blockExpressions.Add(dictVariable);
var block = Expression.Block(new[] { dVariable, dictVariable }, blockExpressions);
return Expression.Lambda<Func<object, Dictionary<string, object>>>(block, inputParameter).Compile();
}
使用简单的测试用例:
public static void Main()
{
var test = new Test() { Foo = "woop", Bar = 3 };
var expr = CreatePropertyDict(typeof(Test));
expr(test).Dump();
}
这里还有一些Expression的高级用法,我就不一一赘述了。查看 the docs,并尝试使用 C# 编译器为 C# 代码的不同位生成的各种表达式。
我有数以千计的数据流,需要转换并添加到列表中。转换通过类似于以下的反射发生
_myObservable.Subscribe(d => {
PropertyInfo[] props = d.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
var propValDict = props.ToDictionary(prop => prop.Name, prop => prop.GetValue(d, null));
myList.Add(propValDict);
});
// Datatype of d is determined during runtime and there are only 8 possibilities of the type
但是这种方法会降低性能,我认为使用反射可能是原因。我正在考虑通过其他方式提高性能。
建议似乎指向表达式树的使用,创建编译的 lambda (Func<object,Dictionary<string, object>>)
并预先将其存储在查找字典中。
//Foreach possibleType in PossibleTypes, Do below
PropertyInfo[] props = possibleType.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
var rootParam = Expression.Parameter(typeof(object), "d");
var param = Expression.Parameter(typeof(PropertyInfo), "prop");
var propertyFirst = Expression.Property(param, "Name");
var param2 = Expression.Parameter(typeof(PropertyInfo), "prop");
var callMethod = Expression.Call(param2, typeof(PropertyInfo).GetMethod(nameof(PropertyInfo.GetValue), new Type[] { typeof(object) }), rootParam);
var pro = Expression.Parameter(typeof(Array), "props");
var toDict = Expression.Invoke(pro, propertyFirst, callMethod);
var lambda = Expression.Lambda<Func<object, Dictionary<string, object>>>(toDict, rootParam);
var compiled = lambda.Compile();
我无法调用 Enumerable 的 ToDictionary class 这种方法我缺少一些东西,或者这真的会提高性能。
请帮忙...
在思考表达式时,您总是需要弄清楚等效的 C# 代码是什么样的。在这种情况下,等效的 C# 代码不会遍历 PropertyInfo
的集合,而是可能看起来像:
public static Func<object, Dictionary<string, object>> CreateConvertToPropertyDict<T>()
{
return input =>
{
var d = (T)input;
return new Dictionary<string, object>())
{
{ "Foo", d.Foo },
{ "Bar", d.Bar },
};
};
}
myList.Add(propValDict);
在表达式领域横向移动,你最终会得到类似的东西:
public static Func<object, Dictionary<string, object>> CreatePropertyDict(Type type)
{
// Consider caching these in a static field, since they're constant
var dictType = typeof(Dictionary<string, object>);
var dictCtor = dictType.GetConstructor(new[] { typeof(int) });
var dictAddMethod = dictType.GetMethod("Add");
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var blockExpressions = new List<Expression>();
// 'object input' is our input parameter
var inputParameter = Expression.Parameter(typeof(object), "input");
// MyType d;
var dVariable = Expression.Variable(type, "d");
// d = (MyType)inputObject;
blockExpressions.Add(Expression.Assign(dVariable, Expression.Convert(inputParameter, type)));
// Dictionary<string, object> dict;
var dictVariable = Expression.Variable(dictType, "dict");
// dict = new Dictionary<string, object>(3) (or however many properties there are)
blockExpressions.Add(Expression.Assign(dictVariable, Expression.New(dictCtor, Expression.Constant(properties.Length))));
foreach (var property in properties)
{
var propertyAccess = Expression.Property(dVariable, property);
// dict.Add("Foo", (object)d.Foo)
blockExpressions.Add(Expression.Call(
dictVariable,
dictAddMethod,
Expression.Constant(property.Name),
Expression.Convert(propertyAccess, typeof(object))));
};
// The final statement in a block is the return value
blockExpressions.Add(dictVariable);
var block = Expression.Block(new[] { dVariable, dictVariable }, blockExpressions);
return Expression.Lambda<Func<object, Dictionary<string, object>>>(block, inputParameter).Compile();
}
使用简单的测试用例:
public static void Main()
{
var test = new Test() { Foo = "woop", Bar = 3 };
var expr = CreatePropertyDict(typeof(Test));
expr(test).Dump();
}
这里还有一些Expression的高级用法,我就不一一赘述了。查看 the docs,并尝试使用 C# 编译器为 C# 代码的不同位生成的各种表达式。