开发者

DynamicObject.TryConvert not called when casting to interface type

开发者 https://www.devze.com 2023-01-11 13:39 出处:网络
The following code throws an exception. TryConvert is not being called for the cast to interface. Why is this? Can I work around the problem?

The following code throws an exception. TryConvert is not being called for the cast to interface. Why is this? Can I work around the problem?

using System.Dynamic;

n开发者_如何学Pythonamespace 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 { }
}


I've found that if you change this line:

var fail = (IFoo)test; 

to this:

IFoo success = test;

it works as expected.

It seems that only an implicit conversion works in this case. Looks like a bug to me.

I also find it very annoying that this fails as well:

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

Because it looks like it should use an implicit conversion too. Another bug?


I suspect it's because in C# (and possibly .NET in general) you can't create a user-defined conversion to an interface type (in the same way that you can't create a user-defined conversion to/from a base/child type). Every interface conversion is therefore treated as a box or a reference conversion.

That really is just a guess though.

EDIT: On the other hand, I've just had a look at the code generated for:

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

and it does generate a dynamic call via Binder.Convert, so it's not the C# compiler doing this. Hmm.


This behavior is explained in the blog post On Dynamic Objects and DynamicObject by Chris Burrows:

"There is another snag in DynamicObject (..) when the call site’s underlying language provides some binding for any operation, that binding overrules any dynamic bindings that might exist.

(..) recall that there is always an explicit conversion from most class types to any interface type in C# (6.2.4, bullet 3), though they can fail. (..)

Just to expand on the interface conversion example a bit, it’s especially weird since if the conversion were implicit (say, try assigning to a local), then the dynamic conversion would have worked. Why? Because the C# binder would have said, “nope! no implicit conversion to IEnumerable,” and then the DynamicObject implementation would have let TryConvert do its thing."

0

精彩评论

暂无评论...
验证码 换一张
取 消