微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

创建用于生成 C# 字典的 lambda 的表达式树

如何解决创建用于生成 C# 字典的 lambda 的表达式树

我有数以千计的数据流,我需要将其转换并添加到列表中。转换通过类似于以下的反射发生

_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,object>>>(toDict,rootParam);
var compiled = lambda.Compile();

我无法调用 Enumerable 类的 ToDictionary 我在这方法中遗漏了一些东西,或者这真的会提高性能吗。

请帮忙...

解决方法

在考虑表达式时,您总是需要弄清楚等效的 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,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,object>>>(block,inputParameter).Compile();
}

使用简单的测试用例:

public static void Main()
{
    var test = new Test() { Foo = "woop",Bar = 3 };
    var expr = CreatePropertyDict(typeof(Test));
    expr(test).Dump();
}

See it on dotnetfiddle

这里有几种更高级的Expression用法,我不打算详细介绍每一种。查看 the docs,并尝试一下 C# 编译器为不同的 C# 代码位生成的表达式种类。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。