explicit cast operator applied to instance created through reflection
I was suprised when found that the following code throws exception at runtime:
class A
{
public string Name { get; set; }
public A()
{
Name = "Class A";
}
}
class B
{
public string Name { get; set; }
public B()
{
Name = "Class B";
}
public static explicit operator A(B source)
{
return new A() {Name = source.Name};
}
}
class Program
{
static void Main(string[] args)
{
// This executes with no error
var bInstance = new B();
Console.WriteLine(bInstance.GetType()); // <assemblyname>.B
var aInstance = (A) bInstance;
Console.WriteLine(aInstance.Name); // Class B
// This fails with InvalidCastException
var bInstanceReflection = Activator.CreateInstance(typeof (B));
Console.WriteLine(bInstanceReflection.GetType()); // <assemblyname>.B
var aInstanceReflection = (A) bInstanceReflection;
Console.WriteLine(aInstanceReflection.Name);
}
}
Could anyone tell me why? I don't really understand what happened
You shouldn't be surprised - custom operators don't override anything, they overload - so they're picked at compile time, not execution time.
When we remove implicit typing from the code, it makes it a bit clearer:
object bInstanceReflection = Activator.CreateInstance(typeof (B));
Console.WriteLine(bInstanceReflection.GetType()); // <assemblyname>.B
A aInstanceReflection = (A) bInstanceReflection;
Now it's reasonably clear that in the final line, (A)
is just a cast from object
which performs the normal reference conversion. No user-defined conversions will be applied at all.
If you're using .NET 4, you can use dynamic typing to get it to work:
// Note the change of type
dynamic bInstanceReflection = Activator.CreateInstance(typeof (B));
Console.WriteLine(bInstanceReflection.GetType()); // <assemblyname>.B
A aInstanceReflection = (A) bInstanceReflection;
Now the conversion is being applied on a dynamic value, which means the choice of what conversion to use is deferred until execution time - at which point it will use your custom operator.
You've created a B
. And then cast it to an A
.
Despite having similar layouts, B has no relationship to A. Static operators are applied by the compiler, but not at runtime via cast. Although the C# syntax is the same, they are very different when dealing with reflection.
This is the normal, expected behaviour.
You can simply change this line:
var bInstanceReflection = Activator.CreateInstance(typeof (B));
To:
var bInstanceReflection = (B)Activator.CreateInstance(typeof (B));
So the compiler now knows the type of bInstanceReflection and can call the correct implitic operator.
精彩评论