用户登录
用户注册

分享至

处理动态时抛出很多第一次机会 Microsoft.CSharp.RuntimeBinderExceptions

  • 作者: 七度空间别样红
  • 来源: 51数据库
  • 2023-02-08

问题描述

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

class Bucket : DynamicObject{只读字典<字符串,对象>m_dict = 新字典<字符串,对象>();public override bool TrySetMember(SetMemberBinder binder, object value){m_dict[binder.Name] = 值;返回真;}public override bool TryGetMember(GetMemberBinder binder, out object result){return m_dict.TryGetValue(binder.Name, out result);}}

现在我这样称呼它:

static void Main(string[] args){动态 d = new Bucket();d.Name = "猎户座";//2个RuntimeBinderExceptionsConsole.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 程序集"Microsoft.CSharp.dll 中出现了Microsoft.CSharp.RuntimeBinder.RuntimeBinderException"类型的第一次机会异常Microsoft.CSharp.dll 中出现了Microsoft.CSharp.RuntimeBinder.RuntimeBinderException"类型的第一次机会异常

任何 访问动态成员的尝试似乎都会向调试日志输出 RuntimeBinderException.虽然我知道第一次机会异常本身并不是问题,但这确实给我带来了一些问题:

  1. 当我正在编写 WPF 应用程序时,我经常将调试器设置为异常中断",否则所有异常最终都会转换为 DispatcherUnhandledException,而所有实际的你想要的信息丢失了.WPF 就是这么烂.

  2. 只要我遇到任何使用 dynamic 的代码,调试输出日志就会变得毫无用处.我关心的所有有用的跟踪行都隐藏在所有无用的 RuntimeBinderExceptions

有什么办法可以关闭它,或者不幸的是 RuntimeBinder 就是这样构建的?

谢谢,猎户座

解决方案

每当解析动态对象的属性时,运行时都会尝试查找在编译时定义的属性.来自 DynamicObject 文档:

<块引用>

您也可以将自己的成员添加到从 DynamicObject 派生的类班级.如果您的班级定义属性,并覆盖TrySetMember 方法,动态语言运行时 (DLR) 首先使用寻找静态语言的活页夹类中属性的定义.如果没有这样的属性,DLR调用 TrySetMember 方法.

RuntimeBinderException 在运行时找不到静态定义的属性时抛出(即在 100% 静态类型的世界中会出现编译器错误).来自 MSDN 文章

<块引用>

...RuntimeBinderException 表示一个在某种意义上没有约束力常见的编译器错误...

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

动态bucket = new ExpandoObject();桶.SomeValue = 45;int value = bucket.SomeValue;//<-- 这里例外

也许 ExpandoObject 可以替代?如果不合适,您需要考虑实施 IDynamicMetaObjectProvider,这就是 ExpandoObject 进行动态调度的方式.但是,它的文档记录不是很好,MSDN 建议您参考 DLR CodePlex 了解更多信息.

I've got a standard 'dynamic dictionary' type class in 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);
    }
}

Now I call it, as follows:

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

The app does what you'd expect it to, but the debug output looks like this:

A first chance exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in Microsoft.CSharp.dll
A first chance exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in Microsoft.CSharp.dll
'ScratchConsoleApplication.vshost.exe' (Managed (v4.0.30319)): Loaded 'Anonymously Hosted DynamicMethods Assembly'
A first chance exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in Microsoft.CSharp.dll
A first chance exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in Microsoft.CSharp.dll

Any attempt to access a dynamic member seems to output a RuntimeBinderException to the debug logs. While I'm aware that first-chance exceptions are not a problem in and of themselves, this does cause some problems for me:

  1. I often have the debugger set to "break on exceptions", as I'm writing WPF apps, and otherwise all exceptions end up getting converted to a DispatcherUnhandledException, and all the actual information you want is lost. WPF sucks like that.

  2. As soon as I hit any code that's using dynamic, the debug output log becomes fairly useless. All the useful trace lines that I care about get hidden amongst all the useless RuntimeBinderExceptions

Is there any way I can turn this off, or is the RuntimeBinder unfortunately just built like that?

Thanks, Orion

解决方案

Whenever a property on a dynamic object is resolved, the runtime tries to find a property that is defined at compile time. From DynamicObject doco:

You can also add your own members to classes derived from the DynamicObject class. If your class defines properties and also overrides the TrySetMember method, the dynamic language runtime (DLR) first uses the language binder to look for a static definition of a property in the class. If there is no such property, the DLR calls the TrySetMember method.

RuntimeBinderException is thrown whenever the runtime cannot find a statically defined property(i.e. what would be a compiler error in 100% statically typed world). From MSDN article

...RuntimeBinderException represents a failure to bind in the sense of a usual compiler error...

It is interesting that if you use ExpandoObject, you only get one exception when trying to use the property:

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

Perhaps ExpandoObject could be an alternative? If it's not suitable you'll need to look into implementing IDynamicMetaObjectProvider, which is how ExpandoObject does dynamic dispatch. However, it is not very well documented and MSDN refers you to the DLR CodePlex for more info.

软件
前端设计
程序设计
Java相关