Question

I have these classes :

class Asset
{    }

class House:Asset
{    }

consider these outsiders static functions :

static void Foo (Asset a) { }
static void Foo (House h) { }

If i write :

House h = new House (...); 
Foo(h);

it will call Foo(House)(compile time binding)

if i write :

Asset a = new House (...);
Foo(a); 

it will call Foo(Asset) (compile time binding)

goal : access the runtime type method :

I have 2 options :

1) using dynamic like this :

 Asset a = new House (...);
 Foo ((dynamic)a); // NOW it will call  Foo(House)

2) move the functions from static to override using polymorphism mechanism.

question :

is there any other way of doing it ( without moving the functions to polymorphism mechanism || dynamic) ?

Was it helpful?

Solution

goal : access the runtime type method

That's what the dynamic keyword is there for. It's actually a pretty really clean & fast way to do multiple dispatch.

Your ultimate options for Multiple Dispatch are

  1. dynamic
  2. Double Dispatch virtual methods
  3. Some hashed anonymous function rule collection
  4. if (x is House) ... else if(x is Asset)...
  5. Reflection -- really slow and ugly

question : is there any other way of doing it ( without moving the functions to polymorphism mechanism || dynamic) ?

So Yes, there are ways of doing that take a lot of work on your part when you could just use dynamic which is fast, less error prone, and really clean syntax wise.

OTHER TIPS

If you want the static entry point, but you also want the polymorphic behavior, then the easiest blend would be to use the singleton pattern. It will give you the static entry point, and the object it returns will have polymorphic methods.

I also suggest ignoring everyone that says a singleton is a horrible, bad thing. Prima Donna singleton woe-preaching is a shibboleth of mid-level developers. A singleton is just a tool, and if it fits your needs - then use it.

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 {
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top