【问题标题】:Why would RuntimeBinderException occur at runtime calling an overloaded function?为什么在运行时调用重载函数时会发生 RuntimeBinderException?
【发布时间】:2019-01-03 09:23:59
【问题描述】:

我终于制作了一个重现此错误的最小示例:

using System;
using Newtonsoft.Json;
class Program
{
    public byte[] Foo(byte[] p) { return new byte[0]; }
    public byte[] Foo(Guid? p) { return new byte[0]; }
    static Guid? ToGuid(string s) { return s == null ? null : (Guid?)new Guid(s); }
    void Bar()
    {
        dynamic d = JsonConvert.DeserializeObject<dynamic>("{}");
        var id = d?.id?.ToString();
        Foo(ToGuid(id));
    }

    static void Main(string[] args)
    {
        new Program().Bar();
    }
}

奇怪的是,当 d.id 为空(或不是字符串)时,它在运行时调用 Foo 时崩溃,说它无法解析要调用的 Foo 版本(以下方法或属性之间的调用不明确)。为什么这不是在编译时解决的呢? dynamic 不应该有什么不同,我可以看到,事实上更奇怪的是,如果我在 ToGuid... 之前添加一个显式强制转换“(Guid?)”,它会按预期工作,同样如果我把它写成:

Guid? id = ToGuid(d.id?.ToString());
Foo(id)

这实际上更有意义。如果我将“var”更改为“string”,它也可以正常工作。

我注意到异常最初是从“System.Linq.Expressions.dll”引发的,这有点奇怪。完整的堆栈跟踪基本上是:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:以下方法或属性之间的调用不明确:“FooService.Foo(byte[])”和“FooService.Foo(System.Guid?)” 在 CallSite.Target(Closure , CallSite , FooService , Object )

异常来源是“匿名托管的 DynamicMethods 程序集”

【问题讨论】:

  • 你不是在Guid? ToGuid(string s) { return uuid == null ? null : new Guid(uuid) }收到编译错误Type of conditional expression cannot be determined because there is no implicit conversion between '&lt;null&gt;' and 'System.Guid'吗?
  • 已修复,抱歉...
  • 那是因为你的例子没有使用动态。只有当 ToGuid( ) 的参数最终来自动态类型变量时才会发生这种情况。
  • 但该站点似乎使用了非常旧的 C# 版本,甚至不允许我写 d?.id.ToString()(这似乎是关键部分)。我正在使用 7.1
  • 可以复制。您的代码在 d 中传递,id 为 null 或任何可以完美运行的代码。

标签: c#


【解决方案1】:

现在我们有了var 变体,我可以重现该问题。问题是nulls。您可能认为ToGuid 的返回类型必须Guid?,因为您假设知道编译器无法使用。就它而言,在Bar 中,它正在查看类型为dynamic1id。这意味着它将假设 whatever ToGuid 返回它将存储在 dynamic 临时变量中。

在这种情况下,它会返回 null,而实际上,dynamic 就是 object。那时,我们已经丢失了关于来自ToGuid 的返回类型的任何编译时间 类型信息。如果不是null,在解析Foo 之前,它会在实例上有效地调用GetType。但这在这里是不可能的。它有null 和两个同样好/坏的候选人,它可以用null 引用来调用。就好像你写了Foo(null);(在编译时会产生等效的错误)。

插入显式转换 - Foo((Guid?)ToGuid(id)); 为运行时提供足够的信息在调用站点,以便能够明确选择您希望它选择的 Foo 的正确重载。


1请记住,无论d 上的id 属性的类型是什么,它都可能有一个ToString 方法来隐藏object 的方法。它不能假设它返回一个string,所以id 也是dynamic

【讨论】:

  • 但是在编译时,ToGuid 只有一种可能的返回类型(无论参数类型如何)——所以我仍然对为什么它不能在编译时解决歧义感到困惑。跨度>
  • 其实也不需要Newtonsoft类型,void Bar() { dynamic id = null; Foo(ToGuid(id)); } 就够了。是的,我明白(现在!)id 是动态类型的。但这并不能正确解释为什么 ToGuid(id) 也是动态类型。
  • @DylanNicholson - 因为dynamic 的全部意义在于将这些决定推迟到运行时。唯一使用 dynamic 变量且 don't 假定 that 表达式的类型也是 dynamic 的表达式是显式转换和构造函数调用。
  • 好吧,我已经勾选了你的答案,即使我不是特别满意,编译器这样做是有道理的。
  • 此外,我现在已经意识到,在我当前的代码中,它也不必要地通过运行时绑定调用 ToGuid。
猜你喜欢
  • 2014-11-11
  • 2013-09-17
  • 2018-02-27
  • 2017-06-20
  • 1970-01-01
  • 2013-02-18
  • 2011-06-19
  • 2020-10-23
  • 2017-01-18
相关资源
最近更新 更多