4
public void Test<T>()
{
    Console.WriteLine(nameof(T));
}

Test<int>();

This code literally prints T and not int, which is not useful at all. I would like to get the name of an actual generic type parameter used without using reflections (typeof and then operate on the Type variable, etc.)

I read that the point of generic is to make a variation of code with different type in their definition all ready at compile time. And nameof is also a compile-time operator. In this case it should be enough to know that T here is an int. There must be some way to do this other than having to do it from the user side (e.g. Test<int>(nameof(int)))

If anyone curious about the use case, besides debugging for example I would like to add things to dictionary using the item's class name as a key. This dictionary has exactly one of each shape.

public AddShape<T>(T shape) where T : Shape
{
    dict.Add(nameof(T), shape.SerializableShape);
}
Yoh Deadfall
  • 2,711
  • 7
  • 28
  • 32
5argon
  • 3,683
  • 3
  • 31
  • 57
  • 1
    `nameof` is for determining the name, why would it give the type? Why can’t you just use the type information of the object? – Sami Kuhmonen Mar 03 '18 at 06:42
  • 4
    T is not always known at compile time. You can use reflection and call MakeGenericMethod with any T : Shape that you like in runtime. – Amir Popovich Mar 03 '18 at 06:44
  • How can you be sure that T is known at compile time? Example: Is T in JSON.Net's `JsonConvert.DeserializeObject(...)` known at compile time? No. – ProgrammingLlama Mar 03 '18 at 06:46
  • @SamiKuhmonen It is a reflection-based way of doing it. I was wondering if there is any non-reflection way because when reading the code (pretending to be a compiler) I feel like I could determine what I have to print. But that is not the case it seems. – 5argon Mar 03 '18 at 06:47
  • @SamiKuhmonen `nameof` can also be used to determining the name of a type. "Used to obtain the simple (unqualified) string name of a variable, type, or member. " (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/nameof) I have use this to avoid magic strings in my code where I have to name something, and those things happens to be related to my designed class name so I build up those strings based on `nameof` and it will update together when using a code refactoring/renaming tools. (eg. `nameof(Shape) + "_current"`, `nameof(Shape) + "_previous")` – 5argon Mar 03 '18 at 07:02

5 Answers5

6

The nameof construct is for determining the name of a type, variable, field, etc. at compile time. It doesn’t help you when you want a type at runtime. What you could do is just use the object’s type information:

public AddShape<T>(T shape) where T : Shape
{
    dict.Add(shape.GetType().FullName, shape.SerializableShape);
}
MarredCheese
  • 17,541
  • 8
  • 92
  • 91
Sami Kuhmonen
  • 30,146
  • 9
  • 61
  • 74
  • Did you mean `typeof(T)`? – Yoh Deadfall Mar 03 '18 at 06:52
  • 2
    Why not add the Type itself as a dictionary's key? – Amir Popovich Mar 03 '18 at 06:54
  • Yes, you could use the type itself, but the original had the name. – Sami Kuhmonen Mar 03 '18 at 06:55
  • @YohDeadfall No, because `typeof` is compile time and using inheritance will mess things up then. You need runtime typing. – Sami Kuhmonen Mar 03 '18 at 06:56
  • @AmirPopovich Sorry but the real version of that simplified example actually is not an ordinary dictionary, but a [Protobuf](https://github.com/google/protobuf)'s `MapField`. Everything needs to be easily serializable to binary, so I need some string to do it. – 5argon Mar 03 '18 at 06:56
  • You said it's for field or variable, it can't be used for type which is not true, you might want be more clear on that as `nameof(Shape)` would work – Ehsan Sajjad Mar 03 '18 at 07:08
  • @EhsanSajjad “variable, field **etc**”. I didn’t say it couldn’t be used for name of a type. But you need to know the type beforehand. Here you don’t. – Sami Kuhmonen Mar 03 '18 at 07:09
  • @SamiKuhmonen Nope, `typeof` works perfectly at runtime. Just try it to be sure: `class Shape { } class Circle : Shape { } class Program { static void WriteType() where T : Shape => Console.WriteLine(typeof(T).Name); static void Main(string[] args) => WriteType(); }` will write Circle. – Yoh Deadfall Mar 03 '18 at 07:53
  • That code wont work with for instance a List containing Circles. It will print out Shape instead of Circle – Barsonax Mar 03 '18 at 08:03
  • @Barsonax Yep, but we don't know how the OP uses the `AddShape` method. I think if he wants to add shapes from an instance of `List` while iterating over it then he doesn't need generics at all, just `shape.GetType()`. John's fix to the code above makes `T` unused. Therefore the is no reason to use generics. – Yoh Deadfall Mar 03 '18 at 08:22
  • The point is your code potentially prints out the wrong type since its based on the type of the generic type variable and not on the actual type (which may derive from the generic type variable). – Barsonax Mar 03 '18 at 08:25
  • @Barsonax You're right, the OP don't need `typeof` either. It's a classical [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). The only thing which could be done here is to write an expanded answer. – Yoh Deadfall Mar 03 '18 at 08:41
  • @Barsonax If you want to notify someone who isn't OP use mentions (`@UserName`). Otherwise only OP gets notified. – Yoh Deadfall Mar 03 '18 at 09:29
2

Because nameof uses type information at compile time and would use the name as string from there but for a generic type T compiler cannot figure that out at compile time as it would be passed as parameter from where it would be consumed as it can be Shape or any subtype of it and that's the reason that it would be initialized at runtime, so that is why it cannot be used like above.

I also found this related which will also help:

https://stackoverflow.com/a/29878933/1875256

Hope it helps

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
2

The documentation isn't very clear about execution time of nameof, but you can find the following sentence:

Unfortunately typeof is not a constant expression like nameof

We also have the specification in the C# language repository which states:

A nameof_expression is a constant expression of type string, and has no effect at runtime.

It's a reason why nameof(T) returns "T" and not the actual type name in your case. Therefore you need another way to obtain the type name at runtime. It's the typeof operator. From the specification:

The typeof operator can be used on a type parameter. The result is the System.Type object for the run-time type that was bound to the type parameter.

class Shape { }
class Circle : Shape { }

class Program
{
    // Output of the following code is Circle
    static void Main(string[] args) => WriteType<Circle>();

    static void WriteType<T>() where T : Shape => Console.WriteLine(typeof(T).Name);
}

As you can see the result fully depends on the bound type of the WriteType<T> method. This means if the bound type is Shape then "Shape" would be printed. Therefore when you iterating over a collection of Shape's the bound type of T would be Shape and not Circle. It's because T is deduced as Shape.

static void Main(string[] args)
{
    var shapes = new Shape[] { new Circle() };
    foreach (var shape in shapes)
        WriteBoundType(shape);
    // Output is "Shape"
}

static void WriteBoundType<T>(T shape)
    where T : Shape => Console.WriteLine(typeof(T).Name);

If you want to obtain the runtime type of the current shape then you should use the GetType method. From the typeof operator documentation:

To obtain the run-time type of an expression, you can use the .NET Framework method GetType, as in the following example

It's your case. You need neither typeof nor nameof. You do not need even generics because you want the runtime type of the current instance.

public AddShape(Shape shape)
{
    dict.Add(shape.GetType().Name, shape.SerializableShape);
    // You also can use the result of GetType directly or its FullName.
    // It depends on your demands which we don't know well.
}

You failed because you have worked with C++ which is statically typed too, but there is a difference. As you know in C++ template arguments are substituted at compile time, but in .NET they are substituted at runtime. I would recommend you to read Differences Between C++ Templates and C# Generics.

Yoh Deadfall
  • 2,711
  • 7
  • 28
  • 32
0

Use typeof(T) as your key, nameof, if it worked, would actually be a horrendous choice because the name it returns is unqualified, so there would be nothing ensuring that the key is unique.

InBetween
  • 32,319
  • 3
  • 50
  • 90
-2

Use typeof(T).Name only ... not need from Shape

Dale K
  • 25,246
  • 15
  • 42
  • 71