在下面的代码示例中,调用l.Add(s)
和c.Add(s)
成功,但是对于通用IList<string>
调用失败。
var l = new List<string>();
dynamic s = "s";
l.Add(s);
var c = (ICollection<string>)l;
c.Add(s);
var i = (IList<string>)l;
i.Add("s"); // works
i.Add(s); // fails
https://dotnetfiddle.net/Xll2If
未处理的异常:Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:方法'Add'的重载没有接受'1'参数 在CallSite.Target(Closure,CallSite,IList`1,Object) 在System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2 [T0,T1](CallSite站点,T0 arg0,T1 arg1) 在C:\ Dev \ PlayGround \ PlayGround \ Program.cs:第13行的Program.Main()中
IList<T>
源自ICollection<T>
。有人可以解释为什么对IList.Add
的呼叫失败吗?
答案 0 :(得分:4)
在查找绑定调用的方法时,编译器(静态或动态)可能必须检查基本类型。
通常使用dynamic
来处理对dynamic
对象本身的方法的调用,因此动态编译器从通过{{1}使用的对象的具体类型开始},只需向上延伸其继承链,直到到达dynamic
来寻找它们。
在将object
对象作为参数传递给静态引用为接口类型的方法调用的情况下,动态绑定器会做同样的事情:如果找不到该方法然后在所讨论的类型上查看该类型的dynamic
,对于接口而言,该BaseType
。因此,它的行为与如果正确检查了继承但未能找到有问题的方法并针对该情况引发适当的异常的行为相同。 (请注意,这也意味着它将找不到在null
上定义的Equals
方法)。
在这种情况下应该要做的是检查所讨论的接口类型实现的所有接口。
此错误是fixed in .NET Core,但该修补程序尚未移植到.NET Framework。如果要针对netfx提交错误报告,则可能要在corefx中引用该修补程序。
有时可以通过定义了所使用方法的基本接口访问接口类型本身或本身object
来解决此问题(因此,无论哪种具体类型实现了该方法,都可以找到合适的方法)。
答案 1 :(得分:0)
这只是(长篇)评论。以下还会产生错误(或密切相关的错误):
public interface IBase
{
void Add(string s);
}
public interface IDerived : IBase
{
}
public class Concrete : IDerived
{
public void Add(string s)
{
}
}
然后:
IDerived i = new Concrete();
i.Add((dynamic)"s");
我敢打赌,这是C#编译器发出的运行时绑定代码中的错误。