简体   繁体   English

C#中使用动态绑定代替严格类型值的扩展方法

[英]The dynamic binding used instead of the extension method for strictly typed value in C#

I have a short-expression that returns an int value and I'm trying to call the extension method on the result value, but when any dynamic variable is present in inner expressions, the dynamic binder is used and I receive an exception Microsoft.CSharp.RuntimeBinder.RuntimeBinderException with the message that我有一个返回 int 值的短表达式,我正在尝试对结果值调用扩展方法,但是当内部表达式中存在任何动态变量时,将使用动态绑定器并且我收到异常Microsoft.CSharp.RuntimeBinder.RuntimeBinderException带有以下消息

'int' does not contain a definition for 'AsStringOrNull' “int”不包含“AsStringOrNull”的定义

Here is all code that reproduces the issue:这是重现该问题的所有代码:

using System;

namespace ExtensionMethodsAndDynamic
{

    static class Extensions
    {
        public static string AsStringOrNull(this object value) => value?.ToString();
    }

    class Program
    {
        public static object Foo(object value) => value;
        static int GetIntFrom(object anything) => 1;


        static void Main()
        {
            dynamic dynamicVariable = null;

            string result1 = GetIntFrom((object)dynamicVariable).AsStringOrNull();// Works as expected
            string result2 = GetIntFrom(dynamicVariable).AsStringOrNull();        
            string result3 = GetIntFrom(Foo(dynamicVariable)).AsStringOrNull();   
        }
    }
}

Also, I noticed another behavior I can not understand.另外,我注意到另一种我无法理解的行为。
The function is successfully called with a strictly typed argument使用严格类型的参数成功调用该函数
The same doesn't work for a dynamic argument这同样不适用于动态参数
But it will work if we specify the function但是如果我们指定函数它会起作用

Please help me to understand why the stuff related to dynamic variables spreads outside the call of this function int GetIntFrom(object anything) .请帮助我理解为什么与动态变量相关的东西在这个函数int GetIntFrom(object anything)的调用之外传播。

Extension methods are evaluated at compile time and replaced with the appropriate static call.扩展方法在编译时被评估并替换为适当的静态调用。 But when you use dynamic , the call is instead resolved at runtime.但是当您使用dynamic ,调用会在运行时解析。 This means that when you type "AsStringOrNull" after a dynamic variable, you are doing something completely different from when you type it after a statically-typed variable.这意味着当您在动态变量后键入“AsStringOrNull”时,您所做的事情与在静态类型变量后键入时完全不同。

After the compiler has finished its work, the code that actually executes works a little bit like this:编译器完成工作后,实际执行的代码有点像这样:

static class Extensions
{
    // Note the absent "this" keyword. It's just an ordinary method after compilation
    public static string AsStringOrNull(object value) => value?.ToString();
}

class Program
{
    public static object Foo(object value) => value;
    static int GetIntFrom(object anything) => 1;


    static void Main()
    {
        dynamic dynamicVariable = null;

        string result1 = Extensions.AsStringOrNull(GetIntFrom((object)dynamicVariable));

        object intObj = GetIntFrom(dynamicVariable); // this step is actually much more complicated
        string result2 = intObj.GetType().GetMethod("AsStringOrNull")?.Invoke(intObj) ?? throw new RuntimeBinderException();
    }
}

For brevity's sake I won't type out what happens for result3 .为简洁起见,我不会输入result3会发生什么。 The above should already make it clear why the exception is thrown.上面应该已经说明了为什么会抛出异常。 You just can't use static compiler sugar when any dynamic variable is involved in method resolution.当方法解析中涉及任何动态变量时,您就不能使用静态编译器糖。

The IDE won't generate any errors here because dynamic objects are for obvious reasons not checked for errors at all. IDE 不会在此处生成任何错误,因为出于显而易见的原因,根本不会检查动态对象是否存在错误。 But if you try to use Go To Reference / F12 on the second and third invocations of "AsStringOrNull", you should see that they aren't resolved.但是,如果您尝试在“AsStringOrNull”的第二次和第三次调用中使用 Go To Reference / F12,您应该看到它们没有被解析。

Now, I think I understand why it works this way.现在,我想我明白为什么它会这样工作。 The compiler produces the code that will use the best overload for this function in runtime based on dynamic parameter true type.编译器生成的代码将在运行时基于动态参数真类型使用此函数的最佳重载。 Since the return type can vary for different overloads it can't be determined at the compile-time, so the following sections (method calls, extension method calls , property access, etc.) can't rely on any strict type and have to assume dynamic.由于不同重载的返回类型可能不同,因此无法在编译时确定,因此以下部分(方法调用、扩展方法调用、属性访问等)不能依赖于任何严格类型,而必须假设动态。

using System;

namespace ExtensionMethodsAndDynamic
{
    class Program
    {
        static int GetIntFrom(object anything) => 1;
        static int GetIntFrom(int anything) => 2;
        static int GetIntFrom(string anything) => 3;
        static string GetIntFrom(object[] anything) => "4";


        static void Main()
        {
            Console.WriteLine(GetIntFrom((dynamic)(new object())));  // 1
            Console.WriteLine(GetIntFrom((dynamic)1));               // 2
            Console.WriteLine(GetIntFrom((dynamic)"1"));             // 3
            Console.WriteLine(GetIntFrom((dynamic)(new object[0]))); // 4
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM