Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

typeof(T) within generic nested types

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:

  1. Why does c.c() produce System.String while c.b() produces System.Int32?

  2. Why does d.d() and d.b() both produce System.String and not behave in exactly the same way as the class C?

like image 954
Petr Hudeček Avatar asked Jan 16 '14 19:01

Petr Hudeček


People also ask

What does the T designator indicate in a generic class?

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.

What is T type in C#?

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.

What is T in VB net?

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 .

What are generic type constraints?

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.


2 Answers

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.

like image 86
Eric Lippert Avatar answered Sep 29 '22 08:09

Eric Lippert


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.

like image 38
SLaks Avatar answered Sep 29 '22 08:09

SLaks