GetType().ToString() returns the FullName of the object. I want the name that you usually use to instantiate that object, i.e, int instead of Int32. Is there a way to do this?
-
Why? anyway you cant... You will have to build a *Dictionary* and map them over yourself. There is no *reflection* way to get a type *alias*... eg `int` instead of `Int32`, as `Int32` is the actual *type* not `int` which is the *alias* – TheGeneral May 29 '19 at 02:30
-
1It's worth noting that not all .NET assemblies are written in c#. If yours wasn't, the variable of interest may not have been declared as an `int`, which is a c# keyword (in Visual Basic it is `Integer`, for example). The string `Int32` is the .NET name for the type agnostic of language. – John Wu May 29 '19 at 02:45
3 Answers
C# has a number of 'types' that are actually keyword aliases to .NET CLR Type
s. In this case, int
is a C# alias for System.Int32
, but the same is true of other C# types like string
which is an alias to System.String
.
This means that when you get under the hood with reflection and start looking at the CLR Type
objects you won't find int
, string
or any of the other C# type aliases because .NET and the CLR don't know about them... and nor should they.
If you want to translate from the CLR type to a C# alias you'll have to do it yourself via lookup. Something like this:
// This is the set of types from the C# keyword list.
static Dictionary<Type, string> _typeAlias = new Dictionary<Type, string>
{
{ typeof(bool), "bool" },
{ typeof(byte), "byte" },
{ typeof(char), "char" },
{ typeof(decimal), "decimal" },
{ typeof(double), "double" },
{ typeof(float), "float" },
{ typeof(int), "int" },
{ typeof(long), "long" },
{ typeof(object), "object" },
{ typeof(sbyte), "sbyte" },
{ typeof(short), "short" },
{ typeof(string), "string" },
{ typeof(uint), "uint" },
{ typeof(ulong), "ulong" },
// Yes, this is an odd one. Technically it's a type though.
{ typeof(void), "void" }
};
static string TypeNameOrAlias(Type type)
{
// Lookup alias for type
if (_typeAlias.TryGetValue(type, out string alias))
return alias;
// Default to CLR type name
return type.Name;
}
For simple types that will work fine. Generics, arrays and Nullable
take a bit more work. Arrays and Nullable
values are handled recursively like this:
static string TypeNameOrAlias(Type type)
{
// Handle nullable value types
var nullbase = Nullable.GetUnderlyingType(type);
if (nullbase != null)
return TypeNameOrAlias(nullbase) + "?";
// Handle arrays
if (type.BaseType == typeof(System.Array))
return TypeNameOrAlias(type.GetElementType()) + "[]";
// Lookup alias for type
if (_typeAlias.TryGetValue(type, out string alias))
return alias;
// Default to CLR type name
return type.Name;
}
This will handle things like:
Console.WriteLine(TypeNameOrAlias(typeof(int?[][])));
Generics, if you need them, are a bit more involved basically the same process. Scan through the generic parameter list and run the types recursively through the process.
Nested Types
When you run TypeNameOrAlias
on a nested type the result is only the name of the specific type, not the full path you'd need to specify to use it from outside the type that declares it:
public class Outer
{
public class Inner
{
}
}
// TypeNameOrAlias(typeof(Outer.Inner)) == "Inner"
This resolves the issue:
static string GetTypeName(Type type)
{
string name = TypeNameOrAlias(type);
if (type.DeclaringType is Type dec)
{
return $"{GetTypeName(dec)}.{name}";
}
return name;
}
// GetTypeName(typeof(Outer.Inner)) == "Outer.Inner"
Generics
Generics in the .NET type system are interesting. It's relatively easy to handle things like List<int>
or Dictionary<int, string>
or similar. Insert this at the top of TypeNameOrAlias
:
// Handle generic types
if (type.IsGenericType)
{
string name = type.Name.Split('`').FirstOrDefault();
IEnumerable<string> parms =
type.GetGenericArguments()
.Select(a => type.IsConstructedGenericType ? TypeNameOrAlias(a) : a.Name);
return $"{name}<{string.Join(",", parms)}>";
}
Now you'll get correct results for things like TypeNameOrAlias(typeof(Dictionary<int, string>))
and so on. It also deals with generic type definitions: TypeNameOrAlias(typeof(Dictionary<,>))
will return Dictionary<TKey,TValue>
.
Where things get difficult is when you nest classes inside generics. Try GetTypeName(typeof(Dictionary<int, string>.KeyCollection))
for an interesting result.

- 15,524
- 2
- 35
- 68
-
Thanks for the detailed response! Why are non simple types handled recursively? – user2793618 May 29 '19 at 15:13
-
1Recursion is required because you can't know how deep the tree goes before you get to a simple type that you want to alias. The example `int?[][]` is 3 levels of recursion to get to the `int`. – Corey May 30 '19 at 01:02
-
GetType().ToString().FromDotNetTypeToCSharpType();
Using the extension method below. I came up with something similar for some templating I was doing in c# using the following Microsoft reference:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/built-in-types-table
Optionally, a boolean whether the type is nullable can be passed in for the shortcut nullable syntax.
/// <summary>Converts a .Net type name to a C# type name. It will remove the "System." namespace, if present,</summary>
public static string FromDotNetTypeToCSharpType(this string dotNetTypeName, bool isNull = false)
{
string cstype = "";
string nullable = isNull ? "?" : "";
string prefix = "System.";
string typeName = dotNetTypeName.StartsWith(prefix) ? dotNetTypeName.Remove(0, prefix.Length) : dotNetTypeName;
switch (typeName)
{
case "Boolean": cstype = "bool"; break;
case "Byte": cstype = "byte"; break;
case "SByte": cstype = "sbyte"; break;
case "Char": cstype = "char"; break;
case "Decimal": cstype = "decimal"; break;
case "Double": cstype = "double"; break;
case "Single": cstype = "float"; break;
case "Int32": cstype = "int"; break;
case "UInt32": cstype = "uint"; break;
case "Int64": cstype = "long"; break;
case "UInt64": cstype = "ulong"; break;
case "Object": cstype = "object"; break;
case "Int16": cstype = "short"; break;
case "UInt16": cstype = "ushort"; break;
case "String": cstype = "string"; break;
default: cstype = typeName; break; // do nothing
}
return $"{cstype}{nullable}";
}

- 1,761
- 17
- 37
This uses CSharpCodeProvider, handles generics and adds namespaces when required.
using System;
using System.CodeDom;
using System.Collections.Generic;
using Microsoft.CSharp;
//...
private string GetFriendlyTypeName(Type type)
{
using (var p = new CSharpCodeProvider())
{
var r = new CodeTypeReference(type);
return p.GetTypeOutput(r);
}
}
Giving credit where it's due.

- 4,554
- 1
- 22
- 37