运行时生成的表达式无法更改字典的值

问题描述 投票:1回答:1

我正在尝试在运行时创建一些表达式以更改给定字典的值。我创建了此代码段,该代码段成功生成了表达式并将其编译为Action。但是调用该操作不能修改字典的值,也不会引发任何错误。这是代码:

public class ChangeDicValue {

    public void Change(IDictionary<string, object> dic) {
        var blocks = MakeCleaningBlock(dic);
        foreach (var block in blocks) 
            block.Invoke(dic);
    }

    private List<Action<IDictionary<string, Object>>> MakeCleaningBlock(IDictionary<string , object > dic) {

        var allKeys = dic.Keys.ToArray();

        var dicType = typeof(IDictionary<,>).MakeGenericType(typeof(string), typeof(object));

        var dicContainsMethod = dicType.GetMethod("ContainsKey", new[] {typeof(string)})
                                ?? throw new InvalidOperationException();

        var actions = new List<Action<IDictionary<string, Object>>>();

        ParameterExpression actionArguments =
            Expression.Parameter(dicType, "actionArguments");

        foreach (var k in allKeys) {

            Expression key = Expression.Constant(k, typeof(string));

            Expression target = Expression.Property(actionArguments, "Item", key);

            var innerStatements = new List<Expression>(Changers);

            var cleanStatements = new List<Expression>();

            foreach (var ins in innerStatements) {
                var assign = Expression.Assign(target, Expression.Block(ins, target));

                cleanStatements.Add(assign);
            }

            Expression body1 = Expression.Block(new List<Expression>(cleanStatements) {target});

            var callToContains = Expression.Call(actionArguments, dicContainsMethod, key);
            var ifThenBody     = Expression.IfThen(callToContains, body1);

            var cleanedValueBlock = Expression.Block(target, ifThenBody, target);

            var assignDic = Expression.Assign(target, cleanedValueBlock);
            // see the debug view of assignDic in UPDATE

            var lambda = Expression.Lambda<Action<IDictionary<string, Object>>>(assignDic, actionArguments);

            var method = lambda.Compile();

            actions.Add(method);
        }

        return actions;
    }


    private static readonly Expression<Func<object, string>>[] Changers
        = {
            s => s + " First changer added.", 
            s => s + " Second changer added."
        };

}

如您所见,这是一个非常简单的代码,不会引起任何错误。你知道我错过了什么吗?

编辑:

[示例字典中一项的变量assignDic的调试视图:

$actionArguments.Item["a"] = .Block() {
    $actionArguments.Item["a"];
    .If (
        .Call $actionArguments.ContainsKey("a")
    ) {
        .Block() {
            $actionArguments.Item["a"] = .Block() {
                .Lambda #Lambda1<System.Func`2[System.Object,System.String]>;
                $actionArguments.Item["a"]
            };
            $actionArguments.Item["a"] = .Block() {
                .Lambda #Lambda2<System.Func`2[System.Object,System.String]>;
                $actionArguments.Item["a"]
            };
            $actionArguments.Item["a"]
        }
    } .Else {
        .Default(System.Void)
    };
    $actionArguments.Item["a"]
}

.Lambda #Lambda1<System.Func`2[System.Object,System.String]>(System.Object $s) {
    $s + " First changer added."
}

.Lambda #Lambda2<System.Func`2[System.Object,System.String]>(System.Object $s) {
    $s + " Second changer added."
}
c# .net-core expression-trees c#-8.0 .net-core-3.1
1个回答
0
投票

确定。终于我找到了问题和解决方案。代码的断点位于内部foreach循环中的分配上,在该循环中,我试图将Expression.Block分配给IndexerExpression。似乎block表达式不会调用它。因此,我通过调用InvokationExpression并传递了Expression.Invoke(命名为IndexerExpression)将其更改为target,现在它就像一个吊饰一样工作:

foreach (var ins in innerStatements) {
    var assign = Expression.Assign(target, Expression.Invoke(ins, target));
    cleanStatements.Add(assign);
}
© www.soinside.com 2019 - 2024. All rights reserved.