如何从表达式树参数数组中获取运行时参数信息

发布于 2024-09-08 10:10:55 字数 3179 浏览 2 评论 0原文

好的,首先解释一下规则:

我需要一个函数来构造一个与任何委托类型匹配的委托,该委托类型封装了一个调用类型为 (Object) (Object[] args) 的委托的函数,其中“args”包含传递给的所有参数调用期间的原始委托。

到目前为止我的工作:

    delegate void TestDelegate(int x, int y);
    delegate object TestDelegate2(object[] args);

    static void Main(string[] sargs)
    {
        TestDelegate d = (TestDelegate)CreateAnonymousFromType(typeof(TestDelegate));
        object ret = d.DynamicInvoke(2, 6);

        if (ret != null) { Console.WriteLine(ret); }

        Console.ReadKey();
    }

    static void SpecialInvoke(int x, int y) 
    {
        Console.WriteLine("x: {0}\r\ny: {1}", x, y);
    }

    static Delegate CreateAnonymousFromType(Type type)
    {
        MethodInfo method = type.GetMethod("Invoke");

        TestDelegate2 _delegate = new TestDelegate2(
            delegate(object[] args) 
            {
                Console.WriteLine("x: {0}\r\ny: {1}", args[0], args[1]);
                return "This is the return";
            });


        var typeargs = CreateArgs(method.GetParameters());
        var argindex = -1;

        var tmp = Expression.Parameter(typeof(Object), "tmp");
        var index = Expression.Parameter(typeof(int), "index");

        var length = Expression.Constant(typeargs.Length);

        var _break = Expression.Label("breakto");

        var delegateargs = Expression.Parameter(typeof(object[]), "args");

        return Expression.Lambda(
            type,
            Expression.Block(
                new[] { tmp, index, delegateargs },
                Expression.Assign(index, Expression.Constant(0)),
                Expression.Assign(delegateargs, Expression.NewArrayBounds(typeof(Object), length)),
                Expression.Loop(
                    Expression.IfThenElse(Expression.LessThan(index, length),
                        Expression.Block(
                            Expression.Assign(tmp, Expression.Convert(typeargs[++argindex], typeof(Object))),
                            Expression.Assign(Expression.ArrayAccess(delegateargs, index), tmp),
                            Expression.PostIncrementAssign(index)
                        ),
                        Expression.Break(_break)
                    ),
                    _break
                ),
                Expression.Call(_delegate.Method, delegateargs)
            ),
            typeargs
        ).Compile();
    }

    static ParameterExpression[] CreateArgs(ParameterInfo[] _params)
    {
        ParameterExpression[] ret = new ParameterExpression[_params.Length];

        for (int i = 0; i < ret.Length; i++)
            ret[i] = Expression.Parameter(_params[i].ParameterType, _params[i].Name);

        return ret;
    }

现在这个 SORTA 有效...我只将参数 x 和 y 的 typeargs[0] 值传递给委托“TestDelegate2”,运行时的“args”参数是 object[] { 2, 2 } 我一生都找不到一种方法来在参数迭代的范围内增加“argindex”...编译时的参数计数是不确定的。 有人知道我该如何解决这个问题吗?

我尝试过使用 Expression.NewArrayInit(typeof(Object), typeargs) 复制参数数组,但失败了,说它不能使用 Int32 来初始化对象数组

我也尝试过: var 参数 = Expression.Constant(typeargs);

并访问“index”处“arguments”的值,但这会产生字符串“x”和“y”..显然是参数的名称而不是它们的值。

老实说,这是我第一次主要尝试使用表达式树,所以任何帮助......无论有多么小。将不胜感激。

谢谢。

Okay first to explain the rules:

I need a function that constructs a delegate matching any delegate type that encapsulates a body of which invokes a delegate of type (Object) (Object[] args) with 'args' containing all of the arguments passed to the original delegate during invocation.

My work so far:

    delegate void TestDelegate(int x, int y);
    delegate object TestDelegate2(object[] args);

    static void Main(string[] sargs)
    {
        TestDelegate d = (TestDelegate)CreateAnonymousFromType(typeof(TestDelegate));
        object ret = d.DynamicInvoke(2, 6);

        if (ret != null) { Console.WriteLine(ret); }

        Console.ReadKey();
    }

    static void SpecialInvoke(int x, int y) 
    {
        Console.WriteLine("x: {0}\r\ny: {1}", x, y);
    }

    static Delegate CreateAnonymousFromType(Type type)
    {
        MethodInfo method = type.GetMethod("Invoke");

        TestDelegate2 _delegate = new TestDelegate2(
            delegate(object[] args) 
            {
                Console.WriteLine("x: {0}\r\ny: {1}", args[0], args[1]);
                return "This is the return";
            });


        var typeargs = CreateArgs(method.GetParameters());
        var argindex = -1;

        var tmp = Expression.Parameter(typeof(Object), "tmp");
        var index = Expression.Parameter(typeof(int), "index");

        var length = Expression.Constant(typeargs.Length);

        var _break = Expression.Label("breakto");

        var delegateargs = Expression.Parameter(typeof(object[]), "args");

        return Expression.Lambda(
            type,
            Expression.Block(
                new[] { tmp, index, delegateargs },
                Expression.Assign(index, Expression.Constant(0)),
                Expression.Assign(delegateargs, Expression.NewArrayBounds(typeof(Object), length)),
                Expression.Loop(
                    Expression.IfThenElse(Expression.LessThan(index, length),
                        Expression.Block(
                            Expression.Assign(tmp, Expression.Convert(typeargs[++argindex], typeof(Object))),
                            Expression.Assign(Expression.ArrayAccess(delegateargs, index), tmp),
                            Expression.PostIncrementAssign(index)
                        ),
                        Expression.Break(_break)
                    ),
                    _break
                ),
                Expression.Call(_delegate.Method, delegateargs)
            ),
            typeargs
        ).Compile();
    }

    static ParameterExpression[] CreateArgs(ParameterInfo[] _params)
    {
        ParameterExpression[] ret = new ParameterExpression[_params.Length];

        for (int i = 0; i < ret.Length; i++)
            ret[i] = Expression.Parameter(_params[i].ParameterType, _params[i].Name);

        return ret;
    }

Now this SORTA works... I only get the value of typeargs[0] passed to the delegate "TestDelegate2" for both parameters x and y, the 'args' parameter at runtime is object[] { 2, 2 } and I can't for the life of me find a way to increment "argindex" inside the scope of the argument iteration... parameter count at compile time is indefinate.
Anybody know how I can solve this?

I've tried just copying the argument array using Expression.NewArrayInit(typeof(Object), typeargs) but that fails saying it can't use Int32 to initialize an array of Object

I've also tried this:
var arguments = Expression.Constant(typeargs);

And accessing the value of "arguments" at "index", however this produces the strings "x" and "y" .. apparently the names of the arguments and not their values.

This is honestly my first major attempt at using expression trees so any help.. no matter how little. Would be appreciated.

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你爱我像她 2024-09-15 10:10:55

我认为您的 Expression.NewArrayInit 走在正确的轨道上。您可以通过使用 Expression.Convert 为每个参数插入转换来修复“不能使用类型为 'System.Int32' 的表达式来初始化类型为 'System.Object' 的数组”错误:

var typeargs = CreateArgs(method.GetParameters());
return Expression.Lambda(
    type,
    Expression.Call(_delegate.Method, Expression.NewArrayInit(typeof(object),
        typeargs.Select(arg => Expression.Convert(arg, typeof(object)))
        )),
    typeargs
).Compile();

I think you were on the right track with Expression.NewArrayInit. You can fix the "An expression of type 'System.Int32' cannot be used to initialize an array of type 'System.Object'" error by using Expression.Convert to insert a conversion for each parameter:

var typeargs = CreateArgs(method.GetParameters());
return Expression.Lambda(
    type,
    Expression.Call(_delegate.Method, Expression.NewArrayInit(typeof(object),
        typeargs.Select(arg => Expression.Convert(arg, typeof(object)))
        )),
    typeargs
).Compile();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文