为什么在运行时调用重载函数时会发生RuntimeBinderException?

时间:2019-01-03 09:23:59

标签: c#

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

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为null(或不是字符串)时,它在运行时调用Foo时崩溃,表示无法解析要调用的Foo版本(以下方法或属性之间的调用不明确)。为什么这在编译时没有解决呢? dynamic不会产生我所能看到的变化,实际上,如果我在(Guid?)之前添加显式强制转换“ ToGuid...”像预期的那样,更奇怪的是,如果相反,我将其写为:

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程序集”

1 个答案:

答案 0 :(得分:0)

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

在这种情况下,它返回null,并且在幕后,dynamic只是object。到那时,我们已经丢失了有关ToGuid中返回类型的所有编译时间类型信息。如果不是null,则在解析Foo之前,它将在实例上有效地调用GetType。但这在这里是不可能的。它有null和两个可以用null引用的同等好/坏候选者。就像您编写了Foo(null);(在编译时会生成等效错误)一样。

插入显式强制转换-Foo((Guid?)ToGuid(id));为运行时提供了足够的信息在呼叫站点,以便能够明确选择要选择的Foo的正确重载。 / p>


1 请记住,无论idd属性的类型是什么,它都有一个ToString方法会遮盖{ {1}}。它不能假设它返回了object,所以string也是id