我终于制作了一个最小的示例来重现此错误:
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程序集”
答案 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 请记住,无论id
上d
属性的类型是什么,它都有一个ToString
方法会遮盖{ {1}}。它不能假设它返回了object
,所以string
也是id
。