I think this is what's happening under the hood of Foo((dynamic)a)
:
Asset a = new House();
Type t = typeof(MainClass);
t.InvokeMember("Foo",
System.Reflection.BindingFlags.InvokeMethod, null,
t, new object[] { a });
That will resolve to Foo(House h)
A quick trip to monodis.exe, without using reflection(e.g. InvokeMember), i.e. using dynamic keyword instead, Asset a = new House(); Foo((dynamic)a)
, this is the IL:
IL_0025: ldstr "Foo"
IL_002a: ldnull
IL_002b: ldtoken MainClass
IL_0030: call class [mscorlib]System.Type class [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
Pretty much what's your hunch shall tell you, the "Foo" is a dead giveaway that dynamic is reflection-y kind of business.
Now, this is sans dynamic, i.e. Asset a = new House(); Foo(a)
:
IL_0010: ldloc.0
IL_0011: call void class MainClass::Foo(class Asset)
The burned instruction is pretty much decided, will not change, it always resolve to Foo(Asset);
Here's the complete code you can use to analyze the dynamic behavior (via monodis.exe or ildasm.exe):
using System;
public class MainClass {
public static void Main() {
Console.WriteLine("Hei");
Asset a = new House();
Foo(a);
Foo((dynamic)a);
object x = 7;
Foo((dynamic)x);
}
public static void Foo(House h) { Console.WriteLine("House"); }
public static void Foo(Asset a) { Console.WriteLine("Asset"); }
public static void Foo(int i) { Console.WriteLine("int"); }
}
public class Asset {
}
public class House : Asset {
}
Output:
Hei
Asset
House
int
This will invoke the Foo overload int, i.e. Foo(int i)
:
object x = 7;
t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null,
t, new object[] { x } );
This would too:
t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null,
t, new object[] { 8 } );
So on your question, what other option you can use, you can use a method that accepts a an untyped object:
public static void FooDynamic(object o)
{
Type t = typeof(MainClass);
t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, t, new object[] { o } );
}
To invoke:
Asset a = new House();
FooDynamic(a); // will select Foo House overload
int i = 7;
FooDynamic(i); // will select Foo int overload
You can also use this API for the code above: public static void Foo(object o)
, then you would have to call Foo like this:
Asset a = new House();
Foo((object)a); // will resolve to House
Given that there's already a dynamic
capability in C# 4, I would be hard-pressed to use reflection, unless the dev is still using C# 3. So there, use the dynamic approach instead :-)
UPDATE
For what it's worth, dynamic
is slow (at least on Mono), when I run this code, there is a considerable delay before the letter "B" appear, about 2 seconds. The dynamic's delay is reproducible even I swap the code order of dynamic and reflection. Reflection's delay is imperceptible, it's faster than dynamic.
using System;
public class MainClass {
public static void Main() {
// there's a delay on initial dynamic call, about two seconds
Test ();
Console.ReadLine ();
// dynamic's speed is instant on subsequent calls,
// as clarified by Eric Lippert, the delegate is cached,
// hence the elimination of delay on subsequent dynamic calls
Test ();
}
public static void Test() {
Asset a = new House();
Console.WriteLine("A");
Foo((dynamic)a); // there is a considerable delay here, the "B" string appears after two seconds
Console.WriteLine ("B");
Type t = typeof(MainClass);
t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, t, new object[] { a } );
Console.WriteLine("C");
}
public static void Foo(House h) { Console.WriteLine("House"); }
public static void Foo(Asset a) { Console.WriteLine("Asset"); }
public static void Foo(int i) { Console.WriteLine("int"); }
}
public class Asset {
}
public class House : Asset {
}