C# Polymorphic types conversion with Generics
Polymorphism has been interviewer's delight for ages. I have been taking interviews for quite some time. And ever since i started preparing question on C# Polymormism, I have been getting surprises. To prepare interview questions, I try writing code, verify it and and then twist the code snippet to make it "interview-able".
If you could get the output of above snippet then you are one of my category who too will be surprised to see the output of my first problem statement.
So finally if you are the filtered one, who is eager to know the answer then please get your self ready to get surprise by answer below:
Surprise surprise...
1. Generic is called for 2 instead of short
2. String is called for "s" instead of generic
The question that comes to mind is:
1. If 2 was considered as Short in our second code, why it was it considered as generic in first code?
2. Why string was not considered as Generic?
The answer to both these question is same. Its the way C# compiler resolves the ambiguity. The compiler always consider any literal digits as integers (Int32 to be specific) while calling a function until and unless the digit is type casted to other compatible type.
The other point to note is, compiler tries to do an implicit conversion before doing any boxing unboxing.This is the reason, 2 was downcasted to short and was not boxed to object in our second example.
So the order of precedence is :
1. Exact type match. (This is the reason why string argument was called with string function and not generic)
2. Generic Match. (this is why 2 was called with Generic and not short in our first example)
This blog is to highlight some of the surprises that i have got while preparing the questions.
The motive of this blog is to highlight tricky points to keep in mind while writing overloaded Functions with Generic parameter.
To explain the concept, I'll start with this short code snippet and would like readers to predict the output without scrolling down to answer.
If you know the output and the reasoning behind it, then you may leave the rest of blog as you probably are better expert than rest of my audience.
And if you are still reading this line it means you are searching for the answer. You will definately get to know the answer, but before i reveal it I would like you to guess the output of below code.
If you could get the output of above snippet then you are one of my category who too will be surprised to see the output of my first problem statement.
And if you couldn't guess the below shown output, it means you need to develop deeper understanding of C# Type conversion and probably this post is of better help than mine:
http://msdn.microsoft.com/en-us/library/ms173105.aspx
http://msdn.microsoft.com/en-us/library/ms173105.aspx
Output 2 |
So finally if you are the filtered one, who is eager to know the answer then please get your self ready to get surprise by answer below:
Output 1 |
Surprise surprise...
1. Generic is called for 2 instead of short
2. String is called for "s" instead of generic
The question that comes to mind is:
1. If 2 was considered as Short in our second code, why it was it considered as generic in first code?
2. Why string was not considered as Generic?
The answer to both these question is same. Its the way C# compiler resolves the ambiguity. The compiler always consider any literal digits as integers (Int32 to be specific) while calling a function until and unless the digit is type casted to other compatible type.
The other point to note is, compiler tries to do an implicit conversion before doing any boxing unboxing.This is the reason, 2 was downcasted to short and was not boxed to object in our second example.
So the order of precedence is :
1. Exact type match. (This is the reason why string argument was called with string function and not generic)
2. Generic Match. (this is why 2 was called with Generic and not short in our first example)
3. Implicit conversion match. (this is why 2 was called with short in our second example)
4. Boxed conversion match. (Note that there is no provision of implicit unbox match, so you can not call a integer function with boxed int argument).
Another interesting observation is, in the second code, the short version is called for -32768 to 32767. As soon as we provide any integer outside this range (say 32768) object version gets called. Obviously this is in line with our order of precedence as no implicit conversion exist to convert 32768 to short.
Hope this was useful. Please feel free to leave relevant feedback.
Happy Coding.
Thanks.
4. Boxed conversion match. (Note that there is no provision of implicit unbox match, so you can not call a integer function with boxed int argument).
Another interesting observation is, in the second code, the short version is called for -32768 to 32767. As soon as we provide any integer outside this range (say 32768) object version gets called. Obviously this is in line with our order of precedence as no implicit conversion exist to convert 32768 to short.
Hope this was useful. Please feel free to leave relevant feedback.
Happy Coding.
Hi Ankush,
ReplyDeletewhen you specify a generic method whithout any restrictions on the generic Type T, you are saying that each type (reference or value type) can be matched as method parameter.
It means that at compile time, if you are not calling the overloaded method with the exact parameter type, the best method overload (whit the exact parameter type) can be used.
So if you try to open your program executable file with the ILDasm tool, you'll find something like the following :
//call Func(2)
IL_0009: callvirt instance void Program::Func<int32>(!!0)
//call Func(2.2)
IL_0010: ldc.r8 2.2000000000000002
IL_0019: callvirt instance void Program::Func<float64>(!!0)
//call Func ("s")
IL_0020: ldstr "S"
IL_0025: callvirt instance void Program::Func(string)
//call Func(new Program())
IL_002c: newobj instance void Program::.ctor()
IL_0031: callvirt instance void Program::Func<class Test.Program>(!!0)
IL_0036: nop
instance void Program::Func
As you can see, at "Compile Time", the best method overload is choosed by using the generic version of the method with the exact parameter type.
Anyway, If you try to call the following method :
int16 x = 2;
Func(x);
the method overload for the short parameter will be used at compile time. Of course this because the compiler found the exact paramter type match, so the generic method version is redundant in this case.
Ciao,
Fabio
P.S.: Nice post ;)
ReplyDelete@Fabio: Thanks for your detailed comments. Really appreciate it.
ReplyDeleteBTW, did you got filter out in the first step or second? :)
Anyways, i mentioned in my post that "any literal digits are considered as integers"
Nice Post indeed!
ReplyDeleteMy personal opinion - If you really want a good candidate - its certainly not something you should be asking in interviews. This is something how compiler interprets the code. You should be asking about designing things using polymorphism - that is something that makes more sense, challenges the design and programming ability of the candidate. Anyways its just my personal opinion, anything which is as bookish as your post is should not be part of the interview.
@Ankush,
ReplyDeleteno I was not the filtered one :-)
Yes, I know you mentioned the right point with a rigorous precision ;)
Ciao,
Fabio
Thanks Anonymous for suggestion, may I know your identity?
ReplyDeleteAgree with MR MRS MISS ANONYMOUS. Language Specific questioning should be given least importance
ReplyDeleteYes agreed!!. Thanks for reading it though.
Delete