首页 > 解决方案 > 为什么在运行时调用重载函数时会发生 RuntimeBinderException?

问题描述

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

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什么不同,我可以看到,事实上更奇怪的是,如果我在它按预期工作(Guid?)之前添加一个显式强制转换 " " ToGuid...,同样如果我把它写成:

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

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

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

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:以下方法或属性之间的调用不明确:CallSite.Target(Closure、CallSite、 FooService , 对象)

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

标签: c#

解决方案


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

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

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


1请记住,无论idon 属性的类型d是什么,它都可能有一种ToString方法可以将object. 它不能假设它也string返回id一个dynamic


推荐阅读