Подтвердить что ты не робот

DynamicObject.TryConvert не вызывается при нажатии на тип интерфейса

Следующий код генерирует исключение. TryConvert не вызывается для трансляции в интерфейс. Почему это? Могу ли я решить проблему?

using System.Dynamic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic test = new JsonNull();
            var ok = (string)test;
            // Next line throws:
            // Unable to cast object of type 'ConsoleApplication1.JsonNull' to type 'ConsoleApplication1.IFoo'.
            var fail = (IFoo)test;
        }
    }

    class JsonNull : DynamicObject
    {
        public override bool TryConvert(ConvertBinder binder, out object result)
        {
            result = null;
            return !binder.Type.IsValueType;
        }
    }

    interface IFoo { }
}
4b9b3361

Ответ 1

Я обнаружил, что если вы измените эту строку:

var fail = (IFoo)test; 

:

IFoo success = test;

работает как ожидалось.

Кажется, что в этом случае работает только неявное преобразование. Похож на ошибку.

Мне также очень досадно, что это тоже не удается:

class Program {
  static void Main(string[] args) {
    dynamic test = new JsonNull();
    Fails(test);
  }
  static void Fails(IFoo ifoo) { }
}
// ...

Потому что похоже, что он должен использовать неявное преобразование. Еще одна ошибка?

Ответ 2

Я подозреваю это, потому что в С# (и, возможно, в .NET вообще) вы не можете создать пользовательское преобразование типа интерфейса (так же, как вы не можете создать пользовательское преобразование в/из тип base/child). Поэтому каждое преобразование интерфейса рассматривается как поле или ссылочное преобразование.

Это действительно просто предположение.

EDIT: С другой стороны, я только что посмотрел код, сгенерированный для:

dynamic d = ...;
IDisposable x = (IDisposable) d;

и он генерирует динамический вызов через Binder.Convert, поэтому это не компилятор С#. Хм.

Ответ 3

Это поведение объясняется в сообщении блога В динамических объектах и ​​DynamicObject Криса Берроуза:

"В DynamicObject (..) есть еще одна загвоздка, когда язык, на котором лежит язык вызовов, предоставляет некоторую привязку для любой операции, которая связывает перекрытие любых динамических привязок, которые могут существовать.

(..) напомним, что всегда существует явное преобразование из большинства типов классов в любой тип интерфейса в С# (6.2.4, bullet 3), хотя они могут терпеть неудачу. (..)

Просто для того, чтобы немного расширить пример преобразования интерфейса, особенно странно, поскольку если преобразование было неявным (скажем, попробуйте назначить локальное), то динамическое преобразование сработало бы. Зачем? Поскольку связующее С# должно было сказать:" Нет, неявное преобразование в IEnumerable ", а затем реализация DynamicObject позволила бы TryConvert выполнить свою задачу".