I don't understand why the following behaves the way it does at all. I don't even know if it's caused by hiding or something else.
class A<T>
{
public class B : A<int>
{
public void b()
{
Console.WriteLine(typeof(T).ToString());
}
public class C : B
{
public void c()
{
Console.WriteLine(typeof(T).ToString());
}
}
public class D : A<T>.B
{
public void d()
{
Console.WriteLine(typeof(T).ToString());
}
}
}
}
class Program
{
static void Main(string[] args)
{
A<string>.B.C c = new A<string>.B.C();
A<string>.B.D d = new A<string>.B.D();
c.c();
c.b();
d.d();
d.b();
}
}
The questions are:
Why does c.c()
produce System.String while c.b()
produces System.Int32
?
Why does d.d()
and d.b()
both produce System.String
and not behave in exactly the same way as the class C
?
Its instances (only one per type exists) are used to represent classes and interfaces, therefore the T in Class<T> refers to the type of the class or interface that the current instance of Class represents.
T is called type parameter, which can be used as a type of fields, properties, method parameters, return types, and delegates in the DataStore class. For example, Data is generic property because we have used a type parameter T as its type instead of the specific data type. Note.
End Sub End Class. In the preceding skeleton, t is a type parameter, that is, a placeholder for a data type that you supply when you declare the class. Elsewhere in your code, you can declare various versions of classHolder by supplying various data types for t .
C# allows you to use constraints to restrict client code to specify certain types while instantiating generic types. It will give a compile-time error if you try to instantiate a generic type using a type that is not allowed by the specified constraints.
This is a variation of a puzzle that I posted on my blog many years ago:
http://blogs.msdn.com/b/ericlippert/archive/2007/07/27/an-inheritance-puzzle-part-one.aspx
and Cyrus posted on his blog before that:
http://blogs.msdn.com/b/cyrusn/archive/2005/08/01/446431.aspx
See the discussion there for details.
Briefly: what does B
mean in class C : B
? Check the container, class B
. Does it contain any type called B
? No. Then check the container's base class. The container's base class is A<int>
. Does it contain anything called B
? Yes. So this means class C : A<int>.B
.
Now we say that c
is A<string>.B.C
. We call method A<string>.B.C.c()
What is T
throughout A<string>
? Obviously string
. So c.c()
prints String
for T
.
Now we call A<string>.B.C.b()
but there is no such method in A<string>.B.C
directly. Where does it get this method? From its base class. What's it's base class? A<int>.B
. So we call A<int>.B.b()
. What is T
throughout A<int>
? Obviously int
.
Now we come to A<string>.B.D.d()
. The base class is irrelevant. T
is string
throughout A<string>
.
And finally A<string>.B.D.b()
. There is no such method on A<string>.B.D
directly so it must get it from its base type. T
is string
throughout A<string>
, so the base type is A<string>.B
. Therefore this calls A<string>.B.b()
.
If that doesn't make sense to you, spell everything out. Let's substitute String for T:
class A_string
{
public class B : A_int
{
public void b()
{
Console.WriteLine(typeof(string).ToString());
}
public class C : A_int.B // Note!
{
public void c()
{
Console.WriteLine(typeof(string).ToString());
}
}
public class D : A_string.B
{
public void d()
{
Console.WriteLine(typeof(string).ToString());
}
}
}
}
OK, that's one of the types. Now let's do the same for int:
class A_int
{
public class B : A_int
{
public void b()
{
Console.WriteLine(typeof(int).ToString());
}
public class C : A_int.B // Note!
{
public void c()
{
Console.WriteLine(typeof(int).ToString());
}
}
public class D : A_int.B
{
public void d()
{
Console.WriteLine(typeof(int).ToString());
}
}
}
}
Now given those types it should be clear what A_string.B.C.c()
, A_string.B.C.b()
, etc, all print out.
A<string>.B.C
inherits A<int>.B
, because B
in the base class declaration comes from the inside parent scope first. (to clarify, its parent scope is A<T>.B
, which contains a type named B
referring to A<int>.B
, inherited from its base class A<int>
)
Calling b()
comes from its base class, in which T
(from the parent scope) is int
.
D
explicitly inherits A<T>.B
, using T
from the outermost scope (A<T>
), so its T
always comes from A<>
in its typename.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With