Why does this code replace float with long?

Viewed 100

I have this code as an example:

class A<T> {
    public class B : A<long> {
        public void f() {
            Console.WriteLine( typeof(T).ToString());
        }
 
    public class C : A<long>.B { }
    
    } 
}

class Prg5 { static void Main() {
    var c = new A<float>.B.C();
    c.f();
} }

With output:

System.Int64

Why does it print the type for long? How and where does the float type passed originally get replaced?

2 Answers

The type C is defined as:

public class C : A<long>.B { }

The type A is defined as:

class A<T> {
    public class B : A<long> {
        public void f() {
            Console.WriteLine( typeof(T).ToString());
        }    

    public class C : A<long>.B { }
    } 
}

So if you create a new C then the type parameter for A is long.

In the statement var c = new A<float>.B.C();, A<float> and B are just parts of the "path" to the nested class C. The fact that A<float> is part of that path does not change the fact that C is an A<long>.B.

The float parameter and the fact that B is an A<long> are not relevant to determine the type of T inside c.f().

Answer

When you call f, you are in a A<T> where T is long because B is a A<long> so T is long.

I see why it seems to be weird: you expect that T is float, but in fact since B is A<long>, B is a A<T> where T is long.

And because C is a child of B, it is an extended same type of B so a A<long>.

Therefore the result for the closed constructed type that output long and not float that was specified for the generic type parameter of the outer class.

So whatever the T specified when creating an instance, you will always get long for the type of T in f.

Solving

It's a little complicated recursive innering, I think, but don't you just simply need that?

class A<T>
{
  public class B : A<T>
  {
    public void f()
    {
      Console.WriteLine(typeof(T).ToString());
    }

    public class C : B { }

  }
}

Thus writing:

var c = new A<float>.B.C();
c.f();

Will output:

System.Single

Readings

Generics open and closed constructed types

Constructed Types

Generics in .NET

Generics (C# Programming Guide)

C# Generics Level 1

C# Generics Level 2

Related