I need some way to get the Name of a Type, when type.IsGenericType
= true
.
Type t = typeof(List);
MessageBox.
Here is my take on this. I did not put the backtick check since for what I see, it's always there. You can add it if you want but I like to keep things simple.
public static string GetFriendlyName(this Type type)
{
if (type.IsGenericType)
{
var name = type.Name.Substring(0, type.Name.IndexOf('`'));
var types = string.Join(",", type.GetGenericArguments().Select(GetFriendlyName));
return $"{name}<{types}>";
}
else
{
return type.Name;
}
}
Type t = ...;
if (t.IsGenericType)
{
Type g = t.GetGenericTypeDefinition();
MessageBox.Show(g.Name); // displays "List`1"
MessageBox.Show(g.Name.Remove(g.Name.IndexOf('`'))); // displays "List"
}
Assuming you just want to see that its List<T>
instead of List<string>
you'd need to do:
MessageBox.Show(t.GetGenericTypeDefinition().FullName)
See http://msdn.microsoft.com/en-us/library/system.type.getgenerictypedefinition.aspx
public static class TypeNameExtensions
{
public static string GetFriendlyName(this Type type)
{
var friendlyName = type.Name;
if (!type.IsGenericType) return friendlyName;
var iBacktick = friendlyName.IndexOf('`');
if (iBacktick > 0) friendlyName = friendlyName.Remove(iBacktick);
var genericParameters = type.GetGenericArguments().Select(x => x.GetFriendlyName());
friendlyName += "<" + string.Join(", ", genericParameters) + ">";
return friendlyName;
}
}
i have improved yoyos version for the usage in Code Generation. Note that all types are now referenced full qualified => global::System.String.
public static string GetFriendlyTypeName(Type type)
{
string friendlyName = type.Name;
if (type.IsGenericType)
{
int iBacktick = friendlyName.IndexOf('`');
if (iBacktick > 0)
{
friendlyName = friendlyName.Remove(iBacktick);
}
friendlyName += "<";
Type[] typeParameters = type.GetGenericArguments();
for (int i = 0; i < typeParameters.Length; ++i)
{
string typeParamName = GetFriendlyTypeName(typeParameters[i]);
friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
}
friendlyName += ">";
friendlyName = "global::" + type.Namespace + "." + friendlyName;
}
else
{
friendlyName = "global::" + type.FullName;
}
return friendlyName.Replace('+', '.');
}
Here is a complete implementation based on the previous answers supporting both Aliases (including Nullable) and Arrays:
public static class TypeNameExtensions
{
public static string GetFriendlyName(this Type type, bool aliasNullable = true, bool includeSpaceAfterComma = true)
{
TryGetInnerElementType(ref type, out string arrayBrackets);
if (!TryGetNameAliasNonArray(type, out string friendlyName))
{
if (!type.IsGenericType)
{
friendlyName = type.Name;
}
else
{
if (aliasNullable && type.GetGenericTypeDefinition() == typeof(System.Nullable<>))
{
string generics = GetFriendlyName(type.GetGenericArguments()[0]);
friendlyName = generics + "?";
}
else
{
string generics = GetFriendlyGenericArguments(type, includeSpaceAfterComma);
int iBacktick = type.Name.IndexOf('`');
friendlyName = (iBacktick > 0 ? type.Name.Remove(iBacktick) : type.Name)
+ $"<{generics}>";
}
}
}
return friendlyName + arrayBrackets;
}
public static bool TryGetNameAlias(this Type type, out string alias)
{
TryGetInnerElementType(ref type, out string arrayBrackets);
if (!TryGetNameAliasNonArray(type, out alias))
return false;
alias += arrayBrackets;
return true;
}
private static string GetFriendlyGenericArguments(Type type, bool includeSpaceAfterComma)
=> string.Join(
includeSpaceAfterComma ? ", " : ",",
type.GetGenericArguments().Select(t => t.GetFriendlyName())
);
private static bool TryGetNameAliasNonArray(Type type, out string alias)
=> (alias = TypeAliases[(int)Type.GetTypeCode(type)]) != null
&& !type.IsEnum;
private static bool TryGetInnerElementType(ref Type type, out string arrayBrackets)
{
arrayBrackets = null;
if (!type.IsArray)
return false;
do
{
arrayBrackets += "[" + new string(',', type.GetArrayRank() - 1) + "]";
type = type.GetElementType();
}
while (type.IsArray);
return true;
}
private static readonly string[] TypeAliases = {
"void", // 0
null, // 1 (any other type)
"DBNull", // 2
"bool", // 3
"char", // 4
"sbyte", // 5
"byte", // 6
"short", // 7
"ushort", // 8
"int", // 9
"uint", // 10
"long", // 11
"ulong", // 12
"float", // 13
"double", // 14
"decimal", // 15
null, // 16 (DateTime)
null, // 17 (-undefined-)
"string", // 18
};
}
Tested with nonsense such as:
var type = typeof(Dictionary<string[,], List<int?[,][]>[,,]>[]);
var name = type.GetFriendlyName();
Console.WriteLine(name);
And it does indeed return: "Dictionary<string[,], List<int?[,][]>[,,]>[]"
Edit: Updated to properly handle enum
types.