处理动态数据时,经常会抛出许多Microsoft.CSharp.RuntimeBinderExceptions的第一次机会异常。

55

我在C#中有一个标准的“动态字典”类型类 -

class Bucket : DynamicObject
{
    readonly Dictionary<string, object> m_dict = new Dictionary<string, object>();

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        m_dict[binder.Name] = value;
        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return m_dict.TryGetValue(binder.Name, out result);
    }
}

现在我这样调用:

static void Main(string[] args)
{
    dynamic d = new Bucket();
    d.Name = "Orion"; // 2 RuntimeBinderExceptions
    Console.WriteLine(d.Name); // 2 RuntimeBinderExceptions
}

这个应用程序的功能和你期望的一样,但调试输出看起来像这样:

在 Microsoft.CSharp.dll 中发生了类型为“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”的第一次机会异常
在 Microsoft.CSharp.dll 中发生了类型为“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”的第一次机会异常
“ScratchConsoleApplication.vshost.exe”(已加载(v4.0.30319)):“匿名主机的 DynamicMethods Assembly” 已加载
在 Microsoft.CSharp.dll 中发生了类型为“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”的第一次机会异常
在 Microsoft.CSharp.dll 中发生了类型为“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”的第一次机会异常

任何尝试访问动态成员似乎都会将RuntimeBinderException输出到调试日志中。我知道第一次机会异常本身并不是问题,但这对我造成了一些麻烦:

  1. 我经常将调试器设置为“断点异常”,因为我正在编写WPF应用程序,否则所有异常最终都会被转换为一个DispatcherUnhandledException,而你想要的所有实际信息都会丢失。WPF就是这样。

  2. 一旦我打开任何使用dynamic的代码,调试输出日志就变得相当无用。我关心的所有有用的跟踪行都被所有无用的RuntimeBinderException隐藏了。

有没有办法关闭这个功能,或者RuntimeBinder不幸就是这样构建的呢?

谢谢,Orion


3
在正常工作环境下,仅使用MVC 3的ViewBag时会出现这种情况。这很让人困扰和不安,因为异常是昂贵的。 - Luke Puplett
我是 .net 世界的新手,正在使用 travelport api 开发旅行社的 webapp。API 是基于 SOAP 的,具有 .wsdl 和 .xsd 文件。我使用 vs015 内置工具创建了代理类。当我使用这些代理类时,所有流程都很顺利,api 给出响应,但在 vs015 的“输出窗口”中,我得到了“Exception thrown: 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' in Microsoft.CSharp.dll”的异常,这个异常的原因是什么,这是正常的 vs015 行为吗?谢谢大家。虽然这不是发布此类评论的地方,但我想从这里向你们寻求帮助。对不起。 - codemilan
3
@codemilan,您问的问题和我7年前2010年的问题一样...向下滚动并查看两个答案,基本上这些异常是使用使用C#中的dynamic关键字构建的内容时显示的正常行为...这似乎是微软C#团队的一个糟糕的设计决策,但这就是现实。 - Orion Edwards
非常感谢您,@OrionEdwards。 - codemilan
2个回答

31

当针对动态对象的属性进行解析时,运行时会尝试查找在编译时定义的属性。根据 DynamicObject 文档:

你也可以将自己的成员添加到从 DynamicObject 类派生的类中。如果你的类定义了属性并覆盖了 TrySetMember 方法,则动态语言运行时(DLR)首先使用语言绑定器查找类中静态定义的属性。如果没有这样的属性,则 DLR 调用 TrySetMember 方法。

每当运行时无法找到静态定义的属性时(即在100%静态类型世界中将是编译器错误),就会抛出 RuntimeBinderException。从MSDN文章中可知:

...RuntimeBinderException 表示无法绑定,这在通常意义上与编译器错误相同...

有趣的是,如果你使用 ExpandoObject,则在尝试使用属性时只会得到一个异常:

dynamic bucket = new ExpandoObject();
bucket.SomeValue = 45;
int value = bucket.SomeValue; //<-- Exception here

或许ExpandoObject可以作为一个备选项?如果它不合适,你需要研究实现IDynamicMetaObjectProvider,这是ExpandoObject执行动态分派的方式。不过,这个接口并没有很好的文档说明,MSDN建议你到DLR CodePlex网站上查找更多信息。


26

这个问题困扰着我,我将异常添加到了异常列表中,以便可以取消选择它。只需按照以下步骤操作:

  • 从“调试”菜单中选择“异常”。
  • 单击右下角的“添加…”按钮。
  • 在类型下拉菜单中选择“Common Language Runtime Exceptions”。
  • 将“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”作为名称输入。
  • 单击“确定”。
  • 异常类型现在将出现在列表中,只需取消选择它即可。

我希望这个设置可以跨解决方案保存,但我认为不能,所以您需要在每个解决方案中重新应用此设置。


10
对我来说,这实际上并没有关闭显示异常的调试日志。它确实让我能够导致调试器在这些异常上中断,但那不是我想要的。我希望它们停止输出。 - tig
我还是没明白。你的意思是我们在 Visual Studio 中看到输出此异常时只需要忽略它吗?这正常吗? - qakmak
我需要关闭这个异常的中断,这很有帮助。谢谢! - Sava B.
2
在Visual Studio 2015中: 调试菜单-> Windows -> 异常设置(Ctrl + Alt + E) - Kasper Jensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接