Множество первых шансов Microsoft.CSharp.RuntimeBinderException, возникающие при работе с динамикой

У меня есть стандартный класс типа "динамический словарь" в С# -

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
}

Приложение выполняет то, что вы ожидаете от него, но вывод отладки выглядит следующим образом:

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

Любая попытка доступа к динамическому члену, похоже, выводит RuntimeBinderException в журналы отладки. Хотя я знаю, что исключения из первого шанса не являются проблемой сами по себе, это вызывает некоторые проблемы для меня:

  • У меня часто есть отладчик, настроенный на "break on exceptions", поскольку я пишу приложения WPF, и в противном случае все исключения заканчиваются тем, что они преобразуются в DispatcherUnhandledException, и вся фактическая информация, которую вы хотите потерять, WPF отстой такой.

  • Как только я нажимаю на любой код, использующий dynamic, журнал вывода отладки становится бесполезным. Все полезные строки трассировки, которые мне интересны, скрываются среди всех бесполезных RuntimeBinderException s

Можно ли отключить это, или RuntimeBinder, к сожалению, только что построенный?

Спасибо, Орион

Ответ 1

Всякий раз, когда свойство динамического объекта разрешено, среда выполнения пытается найти свойство, определенное во время компиляции. Из DynamicObject doco:

Вы также можете добавить своих собственных классы, полученные из DynamicObject класс. Если ваш класс определяет свойства, а также переопределяет Метод TrySetMember, динамический языковая среда (DLR) сначала использует языковое связывание для поиска статического определение свойства в классе. Если такого свойства нет, DLR вызывает метод TrySetMember.

RuntimeBinderException вызывается всякий раз, когда среда выполнения не может найти статически определенное свойство (то есть, что было бы ошибкой компилятора в 100% статически типизированном мире). Из статья MSDN

... RuntimeBinderException представляет собой неспособность связываться в смысле обычная ошибка компилятора...

Интересно, что если вы используете ExpandoObject, вы получаете только одно исключение при попытке использовать свойство:

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

Может быть, ExpandoObject может быть альтернативой? Если это не подходит, вам нужно будет изучить IDynamicMetaObjectProvider, а именно, как ExpandoObject выполняет динамическую отправку. Однако это не очень хорошо документировано, и MSDN ссылается на DLR CodePlex для получения дополнительной информации.

Ответ 2

Это тоже беспокоило меня. Я добавил исключение в список исключений, чтобы отменить его. Просто выполните следующие действия:

  • В меню "Отладка" выберите "Исключения".
  • Нажмите кнопку "Добавить..." в правом нижнем углу.
  • Выберите "Исключения общего времени выполнения языка" в раскрывающемся списке "Тип".
  • Введите "Microsoft.CSharp.RuntimeBinder.RuntimeBinderException" в качестве имени.
  • Нажмите "ОК".
  • В списке появится список исключений. Просто отмените выбор.

Я бы хотел, чтобы этот параметр можно было сохранить в разных решениях, но я не думаю, что это возможно, поэтому вам придется повторно использовать эту настройку для каждого решения.