2010-08-16 2 views
19

Следующий код генерирует исключение. TryConvert не вызывается для трансляции в интерфейс. Почему это? Могу ли я решить проблему?DynamicObject.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 { } 
} 
+0

Что говорит документация? Является ли это поддерживается? – leppie

+0

ли вы когда-нибудь найти способ обойти это? –

+0

Нет, извините, я этого не сделал :( –

ответ

9

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

Это действительно is только предположение, хотя.

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

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

+0

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

+0

@Andrew: Возможно, вы захотите * попробуйте * реализовать 'IDynamicMetaObjectProvider' самостоятельно. Интересно, получилось ли это так далеко. –

12

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

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) { } 
} 
// ... 

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

+0

Он также работает для возвращаемых значений: 'IEnumerable SomeFunc() {dynamic x = ...; return x; } 'вызывает TryConvert на моем динамическом объекте –

6

Такое поведение объясняется в блоге On Dynamic Objects and DynamicObject Крис Берроуз:

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

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

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

Смежные вопросы