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

probem with generics
Schnoogs
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
nigor
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.
agarg
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.
Kevin Jacobson
static public void write<T>(T c)
{
Console.WriteLine(c);
}
William Xie
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.
gonzo883
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
connect2sandeep
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)
ykgreene
static public void Test<T>(T arg)
{
write(arg);
}
Then call...
Test("c");
Test(2);
Manivannan.D.Sekaran
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.
Bassam72
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."
Leedrick_
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.
JawKnee
Bagles1
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