I need some way to get the Name of a Type, when type.IsGenericType = true.
Type t = typeof(List<String>); MessageBox.Show( ..?.. ); What I want, is a message box to pop up with List showing... how can I do that?
You can implement an extension method to get the "friendly name" of a type, like this:
public static class TypeNameExtensions { public static string GetFriendlyName(this 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 = GetFriendlyName(typeParameters[i]); friendlyName += (i == 0 ? typeParamName : "," + typeParamName); } friendlyName += ">"; } return friendlyName; } } With this in your project, you can now say:
MessageBox.Show(t.GetFriendlyName()); And it will display "List<String>".
I know the OP didn't ask for the generic type parameters, but I prefer it that way. ;-)
Namespaces, standard aliases for built-in types, and use of StringBuilder left as an exercise for the reader. ;-)
friendlyName += $"<{string.Join(",", type.GetGenericArguments().Select(p => type.GetFriendlyName()))}>"string typeParamName = typeParameters[i].GetFriendlyName();MyType`1[] and thus the trailing array brackets [] will be removed by friendlyName.Remove(iBacktick);. You need to use type.IsArray to test for this and type.GetArrayRank() to determine how many dimensions the array had.type inside the Select instead of p. ;)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" } List<T> you can use something like this t.GetGenericArguments()[0].Name. I needed this a while ago and couldn't find it anywhere. This will return string in the case you have a List<string>My take on yoyo's approach. Ensures more friendly names for primitives, handles arrays and is recursive to handle nested generics. Also unit tests.
private static readonly Dictionary<Type, string> _typeToFriendlyName = new Dictionary<Type, string> { { typeof(string), "string" }, { typeof(object), "object" }, { typeof(bool), "bool" }, { typeof(byte), "byte" }, { typeof(char), "char" }, { typeof(decimal), "decimal" }, { typeof(double), "double" }, { typeof(short), "short" }, { typeof(int), "int" }, { typeof(long), "long" }, { typeof(sbyte), "sbyte" }, { typeof(float), "float" }, { typeof(ushort), "ushort" }, { typeof(uint), "uint" }, { typeof(ulong), "ulong" }, { typeof(void), "void" } }; public static string GetFriendlyName(this Type type) { string friendlyName; if (_typeToFriendlyName.TryGetValue(type, out friendlyName)) { return friendlyName; } friendlyName = type.Name; if (type.IsGenericType) { int backtick = friendlyName.IndexOf('`'); if (backtick > 0) { friendlyName = friendlyName.Remove(backtick); } friendlyName += "<"; Type[] typeParameters = type.GetGenericArguments(); for (int i = 0; i < typeParameters.Length; i++) { string typeParamName = typeParameters[i].GetFriendlyName(); friendlyName += (i == 0 ? typeParamName : ", " + typeParamName); } friendlyName += ">"; } if (type.IsArray) { return type.GetElementType().GetFriendlyName() + "[]"; } return friendlyName; } [TestFixture] public class TypeHelperTest { [Test] public void TestGetFriendlyName() { Assert.AreEqual("string", typeof(string).FriendlyName()); Assert.AreEqual("int[]", typeof(int[]).FriendlyName()); Assert.AreEqual("int[][]", typeof(int[][]).FriendlyName()); Assert.AreEqual("KeyValuePair<int, string>", typeof(KeyValuePair<int, string>).FriendlyName()); Assert.AreEqual("Tuple<int, string>", typeof(Tuple<int, string>).FriendlyName()); Assert.AreEqual("Tuple<KeyValuePair<object, long>, string>", typeof(Tuple<KeyValuePair<object, long>, string>).FriendlyName()); Assert.AreEqual("List<Tuple<int, string>>", typeof(List<Tuple<int, string>>).FriendlyName()); Assert.AreEqual("Tuple<short[], string>", typeof(Tuple<short[], string>).FriendlyName()); } } if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) return type.GetGenericArguments().First().GetFriendlyName() + "?";[] brackets from the name. GetFriendlyName(typeof(Foo<Bar<Bas>[]>)) returns Foo<Bar<Bas>>.type.IsArray and type.GetElementType() to resolve aliases for arrays. I updated my answer about type aliases to show how one can do that.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; } } 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; } } I know this is an old question, but a colleague and myself needed to do this for some intellisense/roslyn work. The optimal solution appeared to be Ali's solution, but it doesn't work for nested types:
int i = 1; //would work List<string> listTest = new List<string>(); //would work Dictionary<string, int> dictTest = new Dictionary<string, int>(); //would work Dictionary<int, List<string>> nestTest = new Dictionary<int, List<string>>(); //would fail Dictionary<int, List<Dictionary<string, List<object>>>> superNestTest = new Dictionary<int, List<Dictionary<string, List<object>>>>(); //would fail Dictionary<int, List<Dictionary<string, int>>> superNestTest2 = new Dictionary<int, List<Dictionary<string, int>>>(); //would fail In order to solve these issues, I converted the function into a recursive method:
public static class TypeExtensions { public static string GetFriendlyName(this Type type) { string friendlyName = type.FullName; if (type.IsGenericType) { friendlyName = GetTypeString(type); } return friendlyName; } private static string GetTypeString(Type type) { var t = type.AssemblyQualifiedName; var output = new StringBuilder(); List<string> typeStrings = new List<string>(); int iAssyBackTick = t.IndexOf('`') + 1; output.Append(t.Substring(0, iAssyBackTick - 1).Replace("[", string.Empty)); var genericTypes = type.GetGenericArguments(); foreach (var genType in genericTypes) { typeStrings.Add(genType.IsGenericType ? GetTypeString(genType) : genType.ToString()); } output.Append($"<{string.Join(",", typeStrings)}>"); return output.ToString(); } } running for the previous examples/test cases yielded the following outputs:
System.Int32 System.Collections.Generic.List<System.String> System.Collections.Generic.Dictionary<System.String,System.Int32> System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.String>> System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String,System.Collections.Generic.List<System.Object>>>> System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String,System.Int32>>> I spent some time trying to resolve the nested types issue so wanted to document this here to ensure anyone else in future can save some considerable time (and headaches!). I have checked the performance as well, and it is in the microseconds to complete (8 microseconds in the case of the last scenario:
Performance results
(Variables names used from original scenario list)
"i" | 43uS
"listTest" | 3uS
"dictTest" | 2uS
"nestTest" | 5uS
"superNestTest" | 9uS
"superNestTest2" | 9uS
Average times after performing the above code 200 times on each scenario
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.
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('+', '.'); } string GetFriendlyName(Type type) => type.IsArray ? $"{GetFriendlyName(type.GetElementType())}[]" : type.IsGenericType ? $"{type.Name.Remove(type.Name.IndexOf('`'))}<{string.Join(", ", type.GetGenericArguments().Select(GetFriendlyName))}>" : type.Name; Works for nested generic types and arrays of Nullable<T>
//outputs Dictionary<String, List<Nullable<Int32>[]>> GetFriendlyName(typeof(Dictionary<string, List<int?[]>>)); Please, note that most if not all existing answers fails to display array of nullables (int?[])