[英]The dynamic binding used instead of the extension method for strictly typed value in C#
我有一个返回 int 值的短表达式,我正在尝试对结果值调用扩展方法,但是当内部表达式中存在任何动态变量时,将使用动态绑定器并且我收到异常Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
带有以下消息
“int”不包含“AsStringOrNull”的定义
这是重现该问题的所有代码:
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();
}
}
}
另外,我注意到另一种我无法理解的行为。
使用严格类型的参数成功调用该函数
这同样不适用于动态参数
但是如果我们指定函数它会起作用
请帮助我理解为什么与动态变量相关的东西在这个函数int GetIntFrom(object anything)
的调用之外传播。
扩展方法在编译时被评估并替换为适当的静态调用。 但是当您使用dynamic
,调用会在运行时解析。 这意味着当您在动态变量后键入“AsStringOrNull”时,您所做的事情与在静态类型变量后键入时完全不同。
编译器完成工作后,实际执行的代码有点像这样:
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();
}
}
为简洁起见,我不会输入result3
会发生什么。 上面应该已经说明了为什么会抛出异常。 当方法解析中涉及任何动态变量时,您就不能使用静态编译器糖。
IDE 不会在此处生成任何错误,因为出于显而易见的原因,根本不会检查动态对象是否存在错误。 但是,如果您尝试在“AsStringOrNull”的第二次和第三次调用中使用 Go To Reference / F12,您应该看到它们没有被解析。
现在,我想我明白为什么它会这样工作。 编译器生成的代码将在运行时基于动态参数真类型使用此函数的最佳重载。 由于不同重载的返回类型可能不同,因此无法在编译时确定,因此以下部分(方法调用、扩展方法调用、属性访问等)不能依赖于任何严格类型,而必须假设动态。
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.