C# nameof generic type without specifying type
Asked Answered
D

4

66

Assume I have the type

public class A<T> { }

and somewhere in the code I want to throw an exception related to incorrect usage of that type:

throw new InvalidOperationException("Cannot use A<T> like that.");

So far, so good, but I don't want to hardcode the classes name, so I thought I could maybe use

throw new InvalidOperationException($"Cannot use {nameof(A<T>)} like that.");

instead, but in this context I don't know the exact type T. So I thought maybe I could do it with template specialization like in C++:

throw new InvalidOperationException($"Cannot use {nameof(A)} like that.");

or

throw new InvalidOperationException($"Cannot use {nameof(A<>)} like that.");

but those yield

Incorrect number of type parameters.

and

Type argument is missing.


I absolutely don't want to hardcode the classes name for it might change later. How can I get the name of the class, preferably via nameof?

Optimally, what I want to achieve is "Cannot use A<T> like that." or "Cannot use A like that.".

Delwin answered 18/4, 2018 at 11:0 Comment(6)
Just Curious. Is there a reason you don't just add a where constraint on the type of T so that you can't call the method if T is not of some Type?Punchball
@Punchball sure, constraints would be the way to go, but this is not about the type argument, but rather it's like a warning for the developer that they shouldn't use A in that particular case at all.Delwin
nameof(type) is compiled into the assembly, so it is hardcoded, but in the assembly. Runtime it can be considered a constant.Citizen
@Citizen you're right, but when I change A's name in the IDE, it will change all the ``nameof(type)` with it, which would not be a case for a "develop-time" constant.Delwin
@ThomasFlinkow I see, so A<T> is being passed to some method, and that method is throwing the exception because it shouldn't be passed A<T> objects? Just asking since if you want this as a warning to developers, throwing an exception only warns them when they try to run it. It seems like you could resolve this be restricting the Type of T or the types of the parameters used by methods where A<T> is being erroneously passed. Anyway design questions probably off topic to the question.Punchball
@Punchball yeah, you got that quite right. Thanks for the suggestion of overthinking the design :)Delwin
A
41

If you don't care about displaying T, you can just use e.g. nameof(A<object>), assuming object complies with the generic type constraints.

This results in "Cannot use A like that."

If you want to print exactly A<T>, you could use:

$"{nameof(A<T>)}<{nameof(T)}>"

But only from within the class, as T does not exist elsewhere.

Alfeus answered 18/4, 2018 at 11:4 Comment(7)
thank you, this is sufficient and perfectly fine. I will accept this as soon as SO will let me.Delwin
This will give you A<T> though, not A<sometype>Soche
@Soche From what I understand that is what OP is asking for: "Optimally, what I want to achieve is "Cannot use A<T> like that.""Alfeus
Yep, @Alfeus is correct. Sorry to all if that wasn't clear in the question.Delwin
Because almost everything is treated as an object (in an OOP language) you can do it that way.Taxi
@quetzalcoatl: nameof only takes what the specification calls "simple names". It then devotes more than a page to specifying exactly what's simple, but bottom line, open generics aren't simple names. What it calls "unbound generic types" are only allowed in certain contexts, and are treated specially.Teston
Hmm.. interesting. Intellisense fooled me. Before I wrote it, I checked it in VS, I wrote var foo = nameof(List<>) and it didn't get highlighted. It got highlighted only now when I tried building it. Sorry for the confusion. It's not allowed obviously.Sedlik
W
54

Have you tried:

typeof(T).FullName;

or

t.GetType().FullName;

Hope it works for you.

Woolgrower answered 18/4, 2018 at 11:8 Comment(3)
I'm afraid that won't help, because I don't care about T, but rather about A only. Thank you anyways.Delwin
Sorry in thi case.. will becan use.. 'This': this.GetType().FullName. But glad you already get an answerWoolgrower
nameof() evaluates to a const String so it can be used in other const strings and concatenated in string literals - you can't do that with Type.FullName, unfortunately.Tutorial
A
41

If you don't care about displaying T, you can just use e.g. nameof(A<object>), assuming object complies with the generic type constraints.

This results in "Cannot use A like that."

If you want to print exactly A<T>, you could use:

$"{nameof(A<T>)}<{nameof(T)}>"

But only from within the class, as T does not exist elsewhere.

Alfeus answered 18/4, 2018 at 11:4 Comment(7)
thank you, this is sufficient and perfectly fine. I will accept this as soon as SO will let me.Delwin
This will give you A<T> though, not A<sometype>Soche
@Soche From what I understand that is what OP is asking for: "Optimally, what I want to achieve is "Cannot use A<T> like that.""Alfeus
Yep, @Alfeus is correct. Sorry to all if that wasn't clear in the question.Delwin
Because almost everything is treated as an object (in an OOP language) you can do it that way.Taxi
@quetzalcoatl: nameof only takes what the specification calls "simple names". It then devotes more than a page to specifying exactly what's simple, but bottom line, open generics aren't simple names. What it calls "unbound generic types" are only allowed in certain contexts, and are treated specially.Teston
Hmm.. interesting. Intellisense fooled me. Before I wrote it, I checked it in VS, I wrote var foo = nameof(List<>) and it didn't get highlighted. It got highlighted only now when I tried building it. Sorry for the confusion. It's not allowed obviously.Sedlik
M
12

Depending on the place you want to raise that exception, you can use the type.

On the instance, call this.GetType() and then get the Name or FullName property:

throw new InvalidOperationException($"Cannot use {this.GetType().Name} like that.");
Medievalism answered 18/4, 2018 at 11:7 Comment(6)
Thank you, I didn't think of this at all. That would be a good way as well, but AFAIK it resolves the name at runtime rather than at compile time.Delwin
You can do that, but most people don't find A`1 (the real, internal name of the class) very readable.Teston
That doesn't matter too much since type metadata is cache.Medievalism
@ThomasFlinkow Well you don't know what T is until runtime.Soche
@JeroenMostert When derived (B : A<string>), it can be clearer than the other option. So the answer is: it depends.Medievalism
@Soche you're right, but I don't really care about T too much as well. Sorry if that wasn't very clear in the question.Delwin
M
5

typeof(A<>).Name should work.

Monolingual answered 23/3, 2023 at 20:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.