probem with generics

Hi,

I've written the the code below:

static public void Test<T>(T arg)

{

write(arg);

}

static public void write(char c)

{

Console.WriteLine("char");

}

static public void write(int c)

{

Console.WriteLine("int");

}

It should be possible to write like this: Test('c'); or Test(10) and the appropiate write methods should be called. When I try to compile it I get two errors:

Error 1 The best overloaded method match for 'tester.Program.write(char)' has some invalid arguments.

Error 2 Argument '1': cannot convert from 'T' to 'char'.

Is there another way to make the templated method call the appropiate methods. It seems to me that writing something like this in c++ would be no problem

Thanks,

Alex Lehmberg



Answer this question

probem with generics

  • littleflsh

    Yes James I agree.

    We should use mscorlib debugger to verify situation. If I remember correctly, the 'call' always do null pointer check, 'callvirt' don't do it -> maybe we are in a stalemate situation or we need to do brute force timed loop test to evaluate, which one code fragment is cheaper!

    Peca



  • Boot2TheHead

    That's odd. I'm sure I tried a test app with typeof(T) and got a compile error, but I just checked to make sure and it's fine, so yes, you're absolutely right, that's the better way.

  • Reena33

    Right, but generics are different from templates. Overload resolution happens at compile time, but the generic method "instantiation" happens at runtime. Thats why this wont work.



  • Steve1999

    It's the other way around. callvirt throws if you specify a null pointer, call does not. That's why the C# compiler almost always emits callvirt even when calling non-virtual methods.



  • Wayne Sepega

    Try one method like this:

    static public void Test<T>(T arg)

    {

    write(arg);

    }

    Then call...

    Test("c");
    Test(2);



  • Trev72

    Sorry I meant

    static public void write<T>(T c)

    {

    Console.WriteLine(c);

    }


  • Scott Pflanzer

    The constrainted is needed in that case, because we are passing a "T arg"--- a data item of unknown type, which might be a reference or value type.

    In my code, it is passing a known value type (an internal handle), so it won't be boxed anyway.



  • Sweeps78

    Well, on pure size alone, mine wins by 3 bytes (0x15 vs 0x18, or 21 vs 24), and we can see that the last 11 bytes (cooresponding to "== typeof(int)") are identical.

    The key difference is that my version calls System.Type.GetTypeFromHandle(), while the other method calls the virtual function GetType(). Both are direct calls to nonmanaged code, so it's hard to say which woul dbe faster, although GetType has the overhead of a virtual function call. (Personally, I'd expect GetType to call GetTypeFromHandle() internall, but I have no way of proving that)



  • Kardi

    For the very specific example you've given, this code would work:



     static void Main(string[] args)
            {
                try
                {
                    Test(2);
                    Test('c');
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }

                Console.ReadKey();
            }

            static void Test<T>(T arg)
            {
                if(arg.GetType() == typeof(int))
                {
                    Write(int.Parse(arg.ToString()));
                }
                else
                {
                    Write(char.Parse(arg.ToString()));
                }
            }

            static void Write(char c)
            {
                Console.WriteLine(c);
            }

            static void Write(int i)
            {
                Console.WriteLine(i);
            }

     

    I don't know how useful that will be in your real-world issue, sorry.



  • pinkybaby

    EEk...that offend my nature, although I can't see a better way.

    One suggestion I'd make is to change the if () to:

    if(typeof(T) == typeof(int))

    I think the would have a slightly better chance of the JITter realizing that the condition is a constant and just rendering the needed portion.



  • Steve Jackson

    Hi James

    Doing the code snippet and C# compiler seems to be cute (JIT don't do anything magical). Following is result comparing IL code with WinDiff:

    //000027:          {

    //000028:              if ( typeof(T) == typeof( int ) )

    //000028:              if(arg.GetType() == typeof(int))

        IL_0000:   /* D0    | (1B)000001        */ ldtoken     !!T/*1B000001*/

        IL_0005:   /* 28    | (0A)000013        */ call        class [mscorlib/*23000001*/]System.Type/*01000015*/ [mscorlib/*23000001*/]System.Type/*01000015*/::GetTypeFromHandle(valuetype [mscorlib/*23000001*/]System.RuntimeTypeHandle/*01000016*/) /* 0A000013 */

        IL_000a:   /* D0    | (01)000017        */ ldtoken     [mscorlib/*23000001*/]System.Int32/*01000017*/

        IL_000f:   /* 28    | (0A)000013        */ call        class [mscorlib/*23000001*/]System.Type/*01000015*/ [mscorlib/*23000001*/]System.Type/*01000015*/::GetTypeFromHandle(valuetype [mscorlib/*23000001*/]System.RuntimeTypeHandle/*01000016*/) /* 0A000013 */

        IL_0014:   /* 33    | 18                */ bne.un.s    IL_002e

        IL_0000:   /* 0F    | 00                */ ldarga.s    arg

        IL_0002:   /* FE16 | (1B)000001        */ constrained. !!T/*1B000001*/

        IL_0008:   /* 6F    | (0A)000013        */ callvirt    instance class [mscorlib/*23000001*/]System.Type/*01000015*/ [mscorlib/*23000001*/]System.Object/*01000001*/::GetType() /* 0A000013 */

        IL_000d:   /* D0    | (01)000016        */ ldtoken     [mscorlib/*23000001*/]System.Int32/*01000016*/

        IL_0012:   /* 28    | (0A)000014        */ call        class [mscorlib/*23000001*/]System.Type/*01000015*/ [mscorlib/*23000001*/]System.Type/*01000015*/::GetTypeFromHandle(valuetype [mscorlib/*23000001*/]System.RuntimeTypeHandle/*01000017*/) /* 0A000014 */

        IL_0017:   /* 33    | 18                */ bne.un.s    IL_0031

    We can always argue, whitch code is better.

    Peca



  • jdavidroberts

    Thank you Mattias for your correction. I should remember it now forever. I listened some webcast from Richard S** (a .NET main developer whose surname I can't remember for now). I need to look that particular presentation again.

    Peca



  • S Tonstad

    Some thought about your 3 bytes shorter code. the longer code has constrained. prefix for a good reason:

    Quote from standard: " Boxing and unboxing of generic arguments adds performance overhead to a CLI implementation. The constrained. prefix  can improve performance during virtual dispatch to a method defined by a value type, by avoiding boxing the value type."



  • probem with generics