Pop Quiz!
Ok class. Get out your pencils for another pop quiz.
Without using anything beyond your mind, answer the following question:
Consider the following code:
public class A<T1> { public T1 a; public class B<T2> : A<T2> { public T1 b; public class C<T3> : B<T3> { public T1 c; } } }
class PopQuiz { static void Main() { A<int>.B<char>.C<bool> o = new A<int>.B<char>.C<bool>(); System.Console.WriteLine(o.a.GetType().FullName); System.Console.WriteLine(o.b.GetType().FullName); System.Console.WriteLine(o.c.GetType().FullName); } }
What gets printed when you execute the Main method of PopQuiz? No cheating!
Try to figure it out. Write down what you think the answer is, then check it versus what the actual compiled code prints.
For those of you who didn't get it... can you figure out why the answer looks the way it does?
For those of you who did get it (and probably cheated then ;-) ), can you explain how you got your answer?
Good luck!
Comments
Anonymous
August 01, 2005
Should be and is confirmed to be:
System.Bool
System.Char
System.Int32 (unless 64 bit OS?)
----
The reason is that the object o is really an instance of C<int,char,bool> : B<char,bool>
And B is really defined as B<char,bool> : A<bool>
So what really has happened is kinda like a this.
public static void Main()
{
A.B.C o = new A.B.C(0,'b',true);
System.Console.WriteLine(o.a.GetType().FullName);
System.Console.WriteLine(o.b.GetType().FullName);
System.Console.WriteLine(o.c.GetType().FullName);
System.Console.ReadLine();
}
public class A
{
public bool a;
public A(bool a)
{
this.a = a;
}
public class B : A
{
public char b;
public B(char b, bool a) : base(a)
{
this.b = b;
}
public class C : B
{
public int c;
public C(int c, char b, bool a) : base(b,a)
{
this.c = c;
}
}
}
}
That was totally theory and I was surprised that I was right according to the MSIL. :)Anonymous
August 01, 2005
Chris: "The reason is that the object o is really an instance of C<int,char,bool> : B<char,bool> "
Why is that the case? :)Anonymous
August 01, 2005
Because some people really hate us, and take great plesure at seeing us squirm. :)Anonymous
August 01, 2005
Actually, won't you end up with:
System.Int32
System.Int32
System.Int32
while classes A, B and C were used such that
T1:=int, T2:=char, T3:=bool
public fields a, b and c were all declared T1, so therefore we get three ints.
I haven't run it to check though, but they'd have to be all the same.Anonymous
August 01, 2005
The comment has been removedAnonymous
August 02, 2005
Geoff: Yeah, I'm in the same boat. The answer seems simple, but I know there's a catch - I just don't know what it is.
By my reasoning, we instantiate a C<bool>, which inherits from B<bool> which inherits from A<bool> - so we should get System.Boolean for all three printouts. Naturally, though, we don't.
I've gone over the solution Chris Martin gave, and I still don't understand it. I assume the fact that C<bool> turns into C<int,char,bool> has something to do with them being inner classes, but I have no idea why.Anonymous
August 02, 2005
I still think they should all be int32 and if they're not I can't understand why at all.
I agree with Chris Martin that we do actually end up with C<int,char,bool> because the type parameters of the containing type do have to propagate downward into the inner types. But all three variables are declared as T1 which is the type parameter of A, not B or C, and thus should be int throughout.
The only thing I can think of is that perhaps because B<T2> inherits from A<T2>, somehow the definition of T1 is coming from the inheritance relationship, not the containment one. But that seems very wrong, and if it is the case, I'd be inclined to think it's a compiler bug - or a language spec bug - unless I saw strong evidence that there's a reason it should work this way.Anonymous
August 02, 2005
Chris: "The reason is that the object o is really an instance of C<int,char,bool> : B<char,bool> "
Cyrus: "Why is that the case? :)"
I'm just picking straws here but, I'm willing to say it's because C is a public type nested within B which is a nested public type within the public type A. </whew> :)
Type C is a B which is an A. We're not really instantiating a type A or a type B. We're instantiating an object of type C that happens to need three generic types as it's definition because of it's signature. So the generic types kind of just roll down into C.
I'm afraid if that's wrong, I have no idea ;)Anonymous
August 02, 2005
Oh man, this is a confusing one.
I went through several phases - the view expressed in my last comment, then agreement with Anver that they should all be bool, and then I wrestled with it even more and concluded that actually Chris is right after all (as expected, he apparently tested it to confirm)
Even with his explanation it took a LONG time for me to grok what's actually going on. I'm not really adding anything over what Chris already said, but perhaps if I go through the steps in more detail it'll be easier for everyone, me included, to understand.
As I said already, I understood and agreed that we end up with C<int,char,bool> (so that inside C, T1 is int, T2 is char, and T3 is bool). This is necessary so that inside C there are separate notions of T1, T2 and T3, which you need to have. What I didn't understand to start with was how T1 could be anything other than int in that scenario.
The reason, as I was so dimly aware in my previous comment, is that A and B are both carrying out two different roles, from the perspective of C. They are containing classes, and they are also base classes. In these two roles they have different type parameters. In the role as a containing class, T1 is int for all of them, T2 is char, and T3 is bool.
So the member variable "c" is of type int because that's what T1 is at that level.
However, C inherits from B<T3> (or, more precisely, B<T2,T3> because the value of T2 inside the code of C is shared with the code of B). So in its role as a base class, B is instantiated with T1=char and T2=bool. Since the member variables of o come from its base classes (these aren't static variables, after all), the "b" member variable is of type char.
And B inherits from A<T2> (no extra implicit type parameters here because A is the top level). Remember that at this point T2 is bool, as mentioned in the last paragraph. So in its role as a base class, A is being instantiated with T1=bool, and so the member variable "a" is a bool.
Hence the output: Boolean, Char, Int32.
Insanely complex, but it makes perfect sense. I hereby vow never to write code where an inner generic type inherits from its containing class with a different type parameter!Anonymous
August 02, 2005
Man. I didn't explain that very well at all ;)Anonymous
August 02, 2005
Stuart: "I hereby vow never to write code where an inner generic type inherits from its containing class with a different type parameter!"
Hear hear!!!Anonymous
August 02, 2005
I kind of glossed over why C really inherits from B<T2, T3> rather than, say, B<T1, T3>. I'm finding it hard to figure out a clear explanation for this, but I think it's because B inherits from A<T2>, but I can't quite find the words to describe why this should mean that B gets its T1 bound to C's T2. Any help from anyone?Anonymous
August 02, 2005
The comment has been removedAnonymous
August 02, 2005
The comment has been removedAnonymous
August 02, 2005
The comment has been removedAnonymous
August 02, 2005
Stuart:
"
public class A<AT1> {
...public AT1 a;
...public class B<BT1,BT2> : A<BT2> {
......public BT1 b;
......public class C<CT1,CT2,CT3> : B<???,CT3> {
.........public CT1 c;
......}
...}
}
"
Spot on.Anonymous
August 02, 2005
Stuart: If you check out the ECMA spec http://download.microsoft.com/download/8/1/6/81682478-4018-48fe-9e5e-f87a44af3db9/standard.pdf
Section 10.8, you'll see all the rules spelle dout in gory detail. They specify how name lookup occurrs, and why
C : A<T2>.B<T3>Anonymous
August 02, 2005
I didn't get the answer, since I've just read about generics for the 1st time half an hour ago. But I looked at people's answers, and Int32x3 seemed to "make sense" a little bit more.
A<int>.B<char>.C<bool> o = new A<int>.B<char>.C<bool>();
Thus, this means T1 is int.
And since a, b, and c are T1; therefore, they are int.
Not an in-depth explanation so correct me if I'm wrong. =)Anonymous
August 02, 2005
I'm lost. I've read section 10.8 of the ECMA spec over and over again, but I still can't derive the correct answer from that. Chris wrote in his comment that "o is really an instance of C<int,char,bool> : B<char,bool>". But why? And which part of the spec describes that fact?Anonymous
August 03, 2005
loc, if you read my comments, you'll see that I initially thought the same thing, and then gradually came to the realization that I was wrong and why.
Martin, again, my comments went through my gradual path to enlightenment in some detail so you might be able to see what's going on by reading them carefully. I'm not going to repeat everything in there (in particular, the reason it's B<char,bool> is particularly subtle) but I will explain why C is really C<int,char,bool> and not just C<bool> as it might appear.
The underlying MSIL language only has a limited understanding of nested types (actually I'm not completely sure it has any concept of nested types at all). So in order to compile C# code like:
class A {
class B {
}
}
the C# compiler translates it under the hood into something like:
class A {
}
class A.B {
}
This is straightforward in the 1.x world, but when you add generics it's a little more complicated. In this case we have:
class A<T1> {
class B<T2> {
T1 b;
}
}
The C# compiler could try to translate it like this:
class A<T1> {
}
class A.B<T2> {
??? b;
}
but obviously that doesn't work because of the ???. T1 isn't in scope at this point.
So instead the compiler does this:
class A<T1> {
}
class A.B<T1,T2> {
T1 b;
}
That's all relatively straightforward and it's necessary for the implementation of nested types in the presence of generics. The extension of this to the fact that C is really A.B.C<T1,T2,T3> is obvious.
The evil aspect of this particular example is that as well as containing each other, A B and C also inherit from each other. This produces all sorts of subtleties and the best way I can suggest to understand them is to read my previous comments in which I gradually figured out what's going on.Anonymous
August 03, 2005
Martin: "I'm lost. I've read section 10.8 of the ECMA spec over and over again, but I still can't derive the correct answer from that. Chris wrote in his comment that "o is really an instance of C<int,char,bool> : B<char,bool>". But why? And which part of the spec describes that fact?"
Specifically:
o Otherwise, if the namespace-or-type-name appears within the body of a type declaration, then for each instance type T (§25.1.2), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):
...
• Otherwise, if T contains a nested accessible type having name I and K type parameters, then the namespace-or-type-name refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. [Note: Non-type members (constants, fields, methods, properties, indexers, operators, instance constructors, finalizers, and static constructors) and type members with a different number of type parameters are ignored when determining the meaning of the namespace-or-type-name. end note]
---
So here's how lookup (confusingly) works:
Remember that we're starting with:
C<T3> : B<T3>
and we're trying to figure out what C<T3> is trying to derive from.
"then for each instance type T (§25.1.2), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class or struct declaration"
So we look at C<T3> and it doesn't have any nested B<> (not that it would matter since you can't depend on one of your nested types anyways), so we walk up to the enclosing instance type and we start looking at B<T2>. Now we look at the phrase:
"• Otherwise, if T contains a nested accessible type having name I and K type parameters, then the namespace-or-type-name refers to that type constructed with the given type arguments."
and, as it turns out that B<T2> does contain a nested accessible type with that name. HOw??? Well, nested types are accessible through supertypes. So we examine B<T2>'s supertype (which in tihs case is A<T2> and we find the nested type B<>). So we're done.
So the super type of C<T3> is A<T2>.B<T3>.
From that you cna see how all the type params flow.Anonymous
August 03, 2005
Isn't complexity and confusion like this what's responsible for people leaving C++ for Java and .Net? While it's nice for a trivia question, isn't this an indication of something bad?Anonymous
August 03, 2005
After doing some minimal translation of the code, I actually found it works the same way in C++. The program prints out first bool, then char, then int.
Indeed, if one makes a minor modification to the program to discover the types of base objects, one finds that:
o is of type "class A<int>::B<char>::C<bool>"
o's B base is of type "class A<char>::B<bool>"
o's C base is of type "class A<bool>"
The confusion arises because the syntax in which the sample program is written is underqualified. It just says "inherit from B<bool>", but it doesn't say whether this should be "A<int>::B<bool>" with T1=int from the A class containing B; or whether this hould be T1=char from the A class inherited by the B class that contains C.
It so happens that the A<bool> class inherited by the B class that contains C takes precedence, and so we end up inheriting C from A<char>::B<bool>, not from A<int>::B<bool>.Anonymous
August 03, 2005
Sorry, one of the lines in my previous post should be:
o's A base is of type "class A<bool>"Anonymous
August 04, 2005
finally understood clearly =)Anonymous
August 04, 2005
Orion: "Isn't complexity and confusion like this what's responsible for people leaving C++ for Java and .Net? While it's nice for a trivia question, isn't this an indication of something bad? "
Absolutely. But, in the case of generics, it was weighing the badness of conceptual overhead and complex type systems, with the goodness of typesafety and other things.Anonymous
August 06, 2005
The comment has been removedAnonymous
August 25, 2005
denis, you wrote:
> After doing some minimal translation of the code, I actually found it works the same way in C++. The program prints out first bool, then char, then int.
Actually this is not right for C++. Actually it should have been "bool, int, int". VC++7.1 is wrong here. VC++8, GCC and Comeau print "bool, int, int" as expected.Anonymous
August 26, 2005
[url=http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/]上海特价机票[/url]
[url=http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">http://www.88tm.com/">Anonymous
September 06, 2007
Today, the answer to Friday's puzzle . It prints "Int32". But why? Some readers hypothesized that M wouldAnonymous
June 01, 2009
PingBack from http://uniformstores.info/story.php?id=1301