【问题标题】:Add dynamic to IList<T> fails向 IList<T> 添加动态失败
【发布时间】:2018-07-09 07:57:42
【问题描述】:

在以下代码示例中,调用 l.Add(s)c.Add(s) 成功,但调用通用 IList&lt;string&gt; 时失败。

    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(闭包,CallSite,IList`1,对象) 在 System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite 站点,T0 arg0,T1 arg1) 在 C:\Dev\PlayGround\PlayGround\Program.cs:line 13 中的 Program.Main() 处

IList&lt;T&gt; 派生自 ICollection&lt;T&gt;。有人可以解释为什么IList.Add 的调用失败了吗?

【问题讨论】:

  • 真的很奇怪。很好的发现。 i 的类型是IList&lt;dynamic&gt;,不是dynamic,所以看起来i.Add 调用应该在编译时绑定(绑定)。它编译。然而,在运行时,绑定似乎失败了?!对我来说这似乎是一个错误!
  • 显然绑定被推迟到运行时,因为参数s 的类型是dynamic。如果您将s 更改为var s = "s";,则不会出现该错误。无论如何,有趣的是为什么c.Add 运行良好,而i.Add 却不行。
  • 顺便说一句,dynamic 在这里不相关,它与List&lt;string&gt; 相同:"Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'No 方法的重载'Add ' 接受 '1' 个参数'" 我猜是因为 IList&lt;T&gt; is a readonly interface.
  • @JeppeStigNielsen 我已经想通了。我只是想关注显示奇怪行为的最小可能示例。
  • @hvd 这正是它所做的,也正是在 corefx 中修复所必需的。还需要在查找末尾显式添加object

标签: c# dynamic


【解决方案1】:

在查找绑定调用的方法时,编译器(无论是静态的还是动态的)可能必须检查基类型。

通常使用dynamic 我们正在处理对dynamic 对象本身的方法的调用,因此动态编译器从通过dynamic 使用的对象的具体类型开始,并且只需要向上它的继承链直到到达object 来寻找它们。

dynamic 对象作为参数传递给静态引用为接口类型的方法调用的情况下,动态绑定器可悲地做同样的事情:如果它在输入有问题的,然后查看该类型的BaseType,对于接口,它是null。因此,它的行为与正确检查继承但未能找到有问题的方法并针对该情况抛出适当的异常时的行为相同。 (请注意,这也意味着它将无法找到在object 上定义的Equals 方法。

对于这种情况,它应该做的是检查相关接口类型实现的所有接口。

这个错误是fixed in .NET Core,但该修复尚未移植到 .NET Framework。如果您想针对 netfx 提交错误报告,您可能需要在 corefx 中引用该修复。

有时可以通过定义所用方法的基接口访问接口类型本身来解决此问题,或者本身是dynamic(因此在实现它的任何具体类型中都可以找到适当的方法)。

【讨论】:

【解决方案2】:

这只是一个(长)评论。以下也会产生错误(或密切相关的错误):

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# 编译器发出的运行时绑定代码中的错误。

【讨论】:

  • dotnetfiddle.net/yi3KnJ 显示了问题。有趣的是,this tio.run code 没有问题(在 tio.run 中,代码编码在 URL 中;我删除了一些不需要或对问题不重要的 public 修饰符)。这个错误是在哪个版本引入的?
  • 看起来在这两种情况下都使用了相同的 C# 编译器,但是 dotnetfiddle.net 运行时在 Windows 下运行,而 tio.run 在 Linux 下运行。如果有人在 Linux 下拥有 Roslyn C#,他们能否确认 GerdK 的原始问题(来自问题)不会在那里发生?
  • 我认为 trio.run 使用的是 .NET Core,大约一年前我在 .NET Core 中修复了这个错误 :)
  • 在生成任何 IL 或任何表达式之前发生错误(dynamic 首先变为 Expression,然后编译为 IL 或根据其运行位置进行解释)。重要的是正在使用 Microsoft.CSharp 的哪个版本,而不是静态编译所在的 C# 编译器。
  • 刚刚注意到上面@GerdK 说它使用Mono。 Mono 现在使用 corefx 库,但曾经拥有自己的该库版本。所以很可能它有可用的修复程序,但也有可能它使用的是旧的 Mono 版本并且可能从未有过这个错误。 (我根本不知道 Mono 版本,但是 Mono 版本有一些事情是正确的,因为 .NET 版本是错误的,当 Mono 测试开始针对 corefx 版本运行时发现)。跨度>
猜你喜欢
  • 2022-01-17
  • 2020-04-03
  • 2017-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多