代码之家  ›  专栏  ›  技术社区  ›  McGuireV10

通过反射表示Func<dynamic,object>类型

  •  3
  • McGuireV10  · 技术社区  · 6 年前

    给定此方法签名:

    void Foo<T>(Func<T, object> expression)
    

    是否可以使用反射来创建 Func<dynamic, object> 用于 MakeGenericType 对于 expression 参数的类型?“显而易见”的方法不是有效的C#语法,因为 dynamic 既不是类型也不是对象(即。 typeof(dynamic) 是无效的),所以我无法想出任何有用的 ??? 参数如下:

    Type fnType = typeof(Func<,>).MakeGenericType(new Type[] { ???, typeof(object) });
    

    有趣的是,我 可以 这样做,它编译时不会出错,但脚本编译器会在运行时抛出,我认为这是因为 typeof 真的返回了 Func<object, object> :

    Type fn = typeof(Func<dynamic, object>);
    

    至少,通过反射或调试器可以找到的任何东西似乎都无法与 typeof(Func<object, object>) .当然,我意识到 动态 是C#语言中的一个特例--幕后黑箱“魔术”行为以某种方式附加到 object .我想,问题是什么原因 对象 特别是当我写这样的东西时:

    Foo<dynamic>(n => new { n.prop });
    

    自从 动态 倾向于生成一系列“你的体系结构糟糕透顶”的回复,我将通过解释真实场景来抢先回答这些问题:我正在使用Roslyn脚本API从配置加载和编译表达式委托,以过滤、解构或以其他方式更改各种对象(包括匿名类型,因此 动态 )写入结构化记录器(Serilog)。

    我开始认为这是反射无法处理的边缘情况。(我一直希望避免表达,但我想知道这是否能以某种方式实现。)

    编辑:实数代码

    示例输入(实际有效)可能是 Sample.Account (我的测试控制台程序中的一个类)和 a => new { a.Username } 作为要编译的转换表达式,演示了一个存储用户名和密码的帐户类的常见结构化日志记录示例,您可以使用解构来删除密码。(我已经填充了 ScriptingOptions 在调用之前使用必要的程序集引用和导入。)

    来自此的输出(使用上述输入)将是 Func<Sample.Account, object> .问题是如何做到这一点 函数(&L);动态,对象(&T); 作为输出(可以作为源代码编写和编译,但据我所知,不能通过反射进行设置)。

    private static dynamic CompileTransformation(string transformedType, string transformation)
    {
        // get a Type that corresponds to namespace.type in transformedType
        Type TValue = Type.GetType(transformedType) ??
            AppDomain.CurrentDomain.GetAssemblies()
            .Select(a => a.GetType(transformedType))
            .FirstOrDefault(t => t != null);
    
        // get a representation of Func<TValue, object>
        Type funcType = typeof(Func<,>).MakeGenericType(new Type[] { TValue, typeof(object) });
    
        // get a representation of CSharpScript.EvaluateAsync<Func<TValue, object>>()
        var evalMethod = typeof(CSharpScript).GetMethods()
            .FirstOrDefault(m => m.Name.Equals("EvaluateAsync") && m.IsGenericMethod)
            .MakeGenericMethod(funcType);
    
        // execute EvaluateAsync
        dynamic evalTask = evalMethod.Invoke(null, new object[] { transformation, ReflectionHelper.scriptOptions, null, null, null });
        dynamic compiledFunc = evalTask.GetAwaiter().GetResult();
    
        return compiledFunc;
    }
    
    1 回复  |  直到 6 年前
        1
  •  3
  •   Evk    6 年前

    不可能通过反射来实现,因为 dynamic 概念只存在于编译时,而不是运行时。

    如果编译如下内容:

    dynamic x = "test";
    Console.WriteLine(x.Length);
    

    反编译会产生类似DotPeek的结果—您将看到大量神秘的、类似反射的代码,编译器已将您的 动态 密码和 x 是真正的类型 object ,所以整个过程基本上类似于 typeof(string).GetProperty("Length").GetValue(x) ,但可能更有效。但在任何地方你都看不到 动态 它本身

    所以,没有办法 Func<dynamic, object> 从…起 Func<T, object> 在运行时。

    Foo<dynamic>(n => new { n.prop });
    

    在概念上类似于:

    Foo<object>((object n) => new { prop = n.GetType().GetProperty("prop").GetValue(n) });
    

    不幸的是,我不太理解您的用例,尽管我已经尝试过了,所以无法给出合理的建议来替代使用。