I have some custom wrapper types defined with an explicit cast operation:
private class A
{
private readonly int _value;
public A(int value)
{
_value = value;
}
public int Value { get { return _value; } }
}
private class B
{
private readonly int _value;
private B(int value)
{
_value = value;
}
public int Value { get { return _value; } }
public static explicit operator B(A value)
{
return new B(value.Value);
}
}
The following works just fine:
B n = (B)new A(5);
This does not:
B n = (B)(object)new A(5);
// Throws System.InvalidCastException:
// Unable to cast object of type 'A' to type 'B'
...what's going on here?
I'm constrained to have the cast to object
first - the actual cast is being performed elsewhere in generic library code (so it's of the form return (TOutput)(object)input;
, and dynamic
is not an option)
The reason the first code snippet works is that what you see is not a "straight" cast: it is an invocation of an explicit conversion operator. This operation is well-defined, and works exactly the way it is coded: a brand-new object B
is constructed from A
's value. C# has enough information to determine that the explicit operator is to be invoked, because new A(5)
type matches the type of operator B(A value)
parameter.
Once you add a cast to object
, however, the compiler does a true cast. C# no longer has an applicable user-defined conversion operator, so it does the type check, determines that A
is not convertible to B
, and throws an exception.
There is a way to make it work without dynamic
by building and compiling a dynamic lambda, like this:
private static B ConvertFromObject(object a) {
if (a == null) return null;
var p = Expression.Parameter(typeof(object));
var c1 = Expression.Convert(p, a.GetType());
var c2 = Expression.Convert(c1, typeof(B));
var e = (Func<object,B>)Expression.Lambda(c2, p).Compile();
return e(a);
}
Demo.
You could cache compiled lambdas by a
's runtime type to save on the costs of compiling a new expression each time you run the method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With