6

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) ?

Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • Asset a = new House (...); will not call Foo(Asset), cause *runtime* type is House, so it will call Foo(House) – Tigran Apr 26 '12 at 09:49
  • 7
    @Tigran: Overload resolution is performed at compile time. `Asset a = new House(...); Foo(a);` will call `Foo(Asset)` because `a` is declared as `Asset`. – dtb Apr 26 '12 at 09:54
  • @dtb: there is no any *overloading* these are static methods, and it will *not* call Foo(Asset), and even if there is the *sence* of overloading is runtime type identification, and runtime type in the string Asset a = new House() is House. – Tigran Apr 26 '12 at 09:57
  • 5
    @Tigran: There are two methods with the name `Foo` that differ in their list of parameters. [*Overloading*](http://csharpindepth.com/Articles/General/Overloading.aspx) "is what happens when you have two methods with the same name but different signatures. At compile time, the compiler works out which one it's going to call, based on the compile time types of the arguments and the target of the method call." Matters are different if there was an instance-method `virtual void Foo` in `Asset` and an instance-method `override void Foo` in `House` (*Overriding*). – dtb Apr 26 '12 at 10:03
  • @Tigran ive written `outsiders static functions` dtb is right – Royi Namir Apr 26 '12 at 10:04
  • @RoyiNamir: Double dispatch is done in single dispatch languages like C# usually [using the visitor pattern](http://blogs.msdn.com/b/devdev/archive/2005/08/29/457798.aspx). I guess that's what you mean by "polymorphism mechanism"? – dtb Apr 26 '12 at 10:07
  • @dtb: I'm messed your comment before with oevrriding, you are right. :) But, by the way, it will *not* call Foo(Asset), cause *runtime type* is House. – Tigran Apr 26 '12 at 10:07
  • @RoyiNamir: What is right? Did you try to run your code Asset a = new House(); Foo(a) ? And you see that Foo(Asset) called? – Tigran Apr 26 '12 at 10:09
  • @Tigran this code is taken from a book ( c# in nutshell). it gives 2 options to solve it. I asked if theresis more. and without checking it - it does run. – Royi Namir Apr 26 '12 at 10:11
  • @RoyiNamir: well you can try to run your *second* case and you will see that it does *exactly* the same what does first one. The only difference that in second case you specify A like a holder, and not concrete House type, but *runtime* type of both of them is *exaclty* the same, which leads to *exactly* same behavior. Why am I pointing out this? Cause there is no any dispatch in *this* example. – Tigran Apr 26 '12 at 10:15
  • 1
    @Tigran: Here's a short but complete example program: `class Asset { } class House : Asset { } class Program { static void Main() { Asset a = new House(); Foo(a); } static void Foo(Asset a) { Console.WriteLine("Asset"); } static void Foo(House h) { Console.WriteLine("House"); }`. It prints `Asset`. Because there is no dispatch are runtime. Just overload resolution at compile time. – dtb Apr 26 '12 at 10:20
  • @tigran no it doesnt .its just like in my examples ive just tested it. – Royi Namir Apr 26 '12 at 10:22
  • This ruin my notion of burned-in(compile-time resolution) instruction: `Foo((dynamic)a);` I'm thinking what kind of magic is happening under-the-hood of that :-) – Michael Buen Apr 26 '12 at 12:17
  • 10
    @MichaelBuen: The magic is: the compiler emits code that *starts the C# compiler again at runtime*. The runtime version of the compiler analyzes the call *as though the compile-time types of all the objects had been their actual runtime types*, generates an *expression tree* representing that call, compiles the expression tree, caches the delegate for next time, and runs the delegate. The second time you hit this call site, it just calls the delegate. – Eric Lippert Apr 26 '12 at 13:50
  • @EricLippert C# compiler is wonderful and their principals too, comforting to know that it caches the delegate so the subsequent calls shall be faster. Thanks for explaining the computer science behind it, +1 for reminding the term: `call site`. I was supposed to phrase `burned-in instructions at call site` for pondering the kind of magic behind `dynamic` :-) – Michael Buen Apr 26 '12 at 14:50

3 Answers3

9

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.

jbtule
  • 31,383
  • 12
  • 95
  • 128
  • A good compilation of possible solutions.. Thanks.. I have a similar solution mentioned in http://stackoverflow.com/questions/21261519/net-4-0-optimized-code-for-refactoring-existing-if-conditions-and-is-operat/21284844#21284844 – LCJ Jan 22 '14 at 14:10
1

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 {
}
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • Under the hood it's doing crazy stuff that the compiler does to resolve methods static but with the runtime type, compiling on the fly and caching it for future use. Actually your reflection example wouldn't work with overloaded methods, your missing the binder used to pick the correct overload. – jbtule Apr 26 '12 at 13:13
  • au contrarie, it works :-) InvokeMember take care of the business of which overloaded method to invoke. See my edit – Michael Buen Apr 26 '12 at 13:30
  • 1
    Sorry you are correct, InvokeMember works in this case, it's more complicated cases in which it doesn't. But `Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember` which is what the compiler actually swaps out when you use the `dynamic` handles more cases than that reflection method, and does emit IL at runtime to make it sometimes up to 100 faster than reflection. – jbtule Apr 26 '12 at 13:53
  • 1
    @jbtule there is a perceptible delay when using `dynamic` (at least on Mono C#), about two seconds; reflection is instant in my testing, faster than dynamic. See my UPDATE above. I will test the same code on Microsoft C# tomorrow – Michael Buen Apr 26 '12 at 14:18
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/10552/discussion-between-jbtule-and-michael-buen) – jbtule Apr 26 '12 at 14:22
1

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.

Brent Arias
  • 29,277
  • 40
  • 133
  • 234