Tuesday, May 08, 2007

Dynamic Cast in C#

C# allows you to overload the implicit and explicit cast operators to permit your class to be converted to another, either automatically (implicit) or declaratively (explicit). All the necessary conversions are determined and applied by the compiler, at compile time. For example, if you have the following (assume A and B aren't in the same hierarchy):

A a = new B();

The compiler will look to see if B has an implicit cast operator (defined as a static method) and insert it for you if it exists, or fail with a type checking error if it doesn't. Likewise

A a = (A)(new B());

will cause the compiler to insert a call to the explicit cast operator defined in B that casts to A, or fail if it doesn't exist.

This is fine when you only need to handle conversions based on the compile-time type of your objects. If, however, you had written this extremely contrived example:

B b = new B();
object o = b;
A a = b;

This would fail to compile because the compiler is now checking the type "object" for an implicit cast to A, even though the underlying type of o is "B". If you put an explicit cast in "A a = (A)b;" it would then fail at run time, like any other invalid cast. What is really needed is a way to apply cast operators based on the run-time type of the object. This ends up being a pretty simple exercise in reflection.

public static MethodInfo GetMethod(Type toSearch, string methodName,
Type returnType, BindingFlags bindingFlags)
{
return Array.Find(
toSearch.GetMethods(bindingFlags),
delegate(Refl.MethodInfo inf)
{
return ((inf.Name == methodName) && (inf.ReturnType == returnType));
});
}

public static T DynamicCast<T>(object o)
{
Type ot = o.GetType();
MethodInfo meth = GetMethod(ot, "op_Implicit", typeof(T),
BindingFlags.Static | BindingFlags.Public);
if(meth == null)
{
meth = GetMethod(ot, "op_Explicit", typeof(T),
BindingFlags.Static | BindingFlags.Public);
}

if(meth == null) throw new InvalidCastException("Invalid Cast.");

return (T)meth.Invoke(null, new object[] { o });
}

Now we can just do

B b = new B();
object o = b;
A a = DynamicCast<A>(o);

and everything works.

19 comments:

OmegaMan said...

I am not getting your example to work. First off there is a line for your inline delegate that looks like this:

delegate(Refl.MethodInfo inf)

What is Refl (?). If I remove Refl and build with MethodInfo it will compile.

Secondly do these static methods reside in both classes A and B or on a third static class?

Finally I cannot get the line (nuetered so I can publish in this blog the > and < have been replaced by a -)

A a = DynamicCast-B-(o);

to work. It says it cannot implicitly convert type B to A.....what am I missing? (Note I placed the two methods on both A and B to no avail.

Are you leaving something out that can make your code work to someone reading your blog cold? Thanks.

Code Monkey said...

Sorry about that.

Refl is just a namespace alias for System.Reflection. I had a dummy utility class whose name collided. Removing it is the right thing to do.

The GetMethod and DynamicCast methods can be put in your core library classes somewhere. They are basically utility methods.

In order for that line to work, you have to have an implicit conversion to A defined in your class B. If you can get
A a = new B();
to work, then
B b = new B();
object o = b;
A a = DynamicCast-B-(o);
should work.

Are you getting an exception or is it failing to compile?

OmegaMan said...

Doh ... The implicit operator! I had it in my head that it would not be needed. With the implicit defined it works like a charm. Thanks for the article and the response!

Freblast said...

Are you sure it has to be
A a = DynamicCast-B-(o) and not
A a = DynamicCast-A-(o)?

Because when using B as a generic type it still needs the casting operator (A).

Code Monkey said...

You're right. Good catch.

Anonymous said...

Thanks. This almost what I need. Except I'd like to be able to say something like

[A a declared somewhere else]

a = DynamicCast-a.GetType()-(o)

Is that possible?

Code Monkey said...

What type is A?

If you know it at compile time, just write it instead of using a.GetType().

If you don't know it, then you're going to have to use reflection anyway to invoke any methods so DynamicCast won't really buy you anything.

Nevertheless, if you still have a scenario where you think this would be useful, just change the method to be non generic and take a Type as the first parameter.

e.g.

public static T DynamicCast(Type ot, object o)

Anonymous said...

In my case, A would be a value type. Basically, I am getting data from SQL Server database using a SqlDataReader and populating properties of a business object with it. Normally I would do something like

AnObject.PropA = (int)sqlReader["colA"]

I would like to avoid hardcoding the type of PropA in the cast.

I'll try your suggestion of passing the type in as the parameter.

Code Monkey said...

You might be better off using Convert.ChangeType, rather than dynamic cast.

hun said...

Hello!
Inspite of the age of this article :) , I have exatly same problem and your article seems very useful, but I have to make the dynamic cast in the silverlight 2 envinroment, and there is not Array.find() .
Bacause I'm quite newby in the .Net, and C#, I have no idea how to translate the GetMethod() method to leverage without Array.Find().
Could You help me in this?
Thanks Bts

Code Monkey said...

This code was written for .net 2 so you have a few more options.

Instead of Array.Find(toSearch.GetMethods()...

you can use the Linq enumerable extensions. Make sure
using System.Linq;
is at the top of your file and then try

return toSearch.GetMethods().FirstOrDefault(inf => ((inf.Name == methodName) && (inf.ReturnType == returnType)));

This is the c# 3.5 way to do things. FirstOrDefault is an Extension Method on IEnumerable. That little "inf => ((...))" thing is a Lambda expression, which is similar to the anonymous delegate I used in the previous example. Since you say you're new to c#, it's definitely worth learning about these concepts since they are very powerful.

If you want to opt for simplicity, however, you can always do something like this:

foreach(MethodInfo inf in toSearch.GetMethods())
{
if ((inf.Name == methodName) && (inf.ReturnType == returnType)) return inf;
}

return null;


which is basically all that fancy code is doing.

Bts said...
This comment has been removed by the author.
Bts said...

Dear Cm! thank Your help very much!
I wasn't able to understand, why the signature doesn't fit:) .
My main problem was, to access a grid's properties of a silverlight user control from the base class. Your working solution made realise me, my approach was wrong. Finally I made a shortcut, and saved the reference to the grid in the base class. So in this way doest need dinamically casting (depending on the type of the child class). Thanks again, Bts

Anonymous said...

The lambda expression above has some problems. I thought I'd post one that works:

return Array.Find(toSearch.GetMethods(), inf => ((inf.Name == methodName) && (inf.ReturnType == returnType)));

Anonymous said...

wonder what's the real benefit/difference between your approach and the simple "A a = (A)b;"? thanks.

Code Monkey said...

The beginning of the post explains why.

The compiler uses the compile-time types to determine whether b has the appropriate cast operator defined.

Dynamic Cast is for using the run-time type.

yonadav said...

This is my take on this, hope it helps anyone:

public static object DynamicCast(object obj, Type targetType) {
// First, it might be just a simple situation
if (targetType.IsAssignableFrom(obj.GetType()))
return obj;

// If not, we need to find a cast operator. The operator
// may be explicit or implicit and may be included in
// either of the two types...
BindingFlags pubStatBinding =
BindingFlags.Public | BindingFlags.Static;
Type originType = obj.GetType();
String[] names = {"op_Implicit", "op_Explicit"};

MethodInfo castMethod =
targetType.GetMethods(pubStatBinding)
.Union(originType.GetMethods(pubStatBinding))
.FirstOrDefault(
itm =>
itm.ReturnType.Equals(targetType)
&&
itm.GetParameters().Length == 1
&&
itm.GetParameters()[0].ParameterType.IsAssignableFrom(originType)
&&
names.Contains(itm.Name)
);
if (null != castMethod)
return castMethod.Invoke(null, new object[] {obj});
else
throw new InvalidOperationException(
String.Format(
"No matching cast operator found from {0} to {1}.",
originType.Name,
targetType.Name));

}

Michael Ceranski said...

I am dynamically casting objects using Reflection in an ASP.NET MVC app. See my blog post
http://www.codecapers.com/post/Dynamic-Casting-with-NET.aspx

boyparka said...

Thanks for the sample code - very informative. Also thanks for pointing me towards Convert.ChangeType in one of your comments - that was exactly what I was looking for!