Get the type name

后端 未结 10 817
星月不相逢
星月不相逢 2020-12-05 23:05

How i can get full right name of generic type?

For example: This code

typeof(List).Name

return

相关标签:
10条回答
  • 2020-12-05 23:28

    Here's my implementation, which benefited from @Hans's answer above and @Jack's answer on a duplicate question.

    public static string GetCSharpName( this Type type )
    {
        string result;
        if ( primitiveTypes.TryGetValue( type, out result ) )
            return result;
        else
            result = type.Name.Replace( '+', '.' );
    
        if ( !type.IsGenericType )
            return result;
        else if ( type.IsNested && type.DeclaringType.IsGenericType )
            throw new NotImplementedException();
    
        result = result.Substring( 0, result.IndexOf( "`" ) );
        return result + "<" + string.Join( ", ", type.GetGenericArguments().Select( GetCSharpName ) ) + ">";
    }
    
    static Dictionary<Type, string> primitiveTypes = 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(sbyte), "sbyte" },
        { typeof(short), "short" },
        { typeof(string), "string" },
        { typeof(uint), "uint" },
        { typeof(ulong), "ulong" },
        { typeof(ushort), "ushort" },
    };
    
    0 讨论(0)
  • 2020-12-05 23:28

    Came across this and thought I'd share my own solution. It handles multiple generic arguments, nullables, jagged arrays, multidimensional arrays, combinations of jagged/multidimensional arrays, and any nesting combinations of any of the above. I use it mainly for logging so that it's easier to identify complicated types.

    public static string GetGoodName(this Type type)
    {
        var sb = new StringBuilder();
    
        void VisitType(Type inType)
        {
            if (inType.IsArray)
            {
                var rankDeclarations = new Queue<string>();
                Type elType = inType;
    
                do
                {
                    rankDeclarations.Enqueue($"[{new string(',', elType.GetArrayRank() - 1)}]");
                    elType = elType.GetElementType();
                } while (elType.IsArray);
    
                VisitType(elType);
    
                while (rankDeclarations.Count > 0)
                {
                    sb.Append(rankDeclarations.Dequeue());
                }
            }
            else
            {
                if (inType.IsGenericType)
                {
                    var isNullable = inType.IsNullable();
                    var genargs = inType.GetGenericArguments().AsEnumerable();
                    var numer = genargs.GetEnumerator();
    
                    numer.MoveNext();
    
                    if (!isNullable) sb.Append($"{inType.Name.Substring(0, inType.Name.IndexOf('`'))}<");
    
                    VisitType(numer.Current);
    
                    while (numer.MoveNext())
                    {
                        sb.Append(",");
                        VisitType(numer.Current);
                    }
    
                    if (isNullable)
                    {
                        sb.Append("?");
                    }
                    else
                    {
                        sb.Append(">");
                    }
                }
                else
                {
                    sb.Append(inType.Name);
                }
            }
        }
    
        VisitType(type);
    
        var s = sb.ToString();
    
        return s;
    }
    

    This:

    typeof(Dictionary<int?, Tuple<string[], List<string[][,,,]>>>).GetGoodName()
    

    ...returns this:

    Dictionary<Int32?,Tuple<String[],List<String[][,,,]>>>
    
    0 讨论(0)
  • 2020-12-05 23:30
    typeof(List<string>).ToString()
    
    0 讨论(0)
  • 2020-12-05 23:34

    I had problems with the other answers in some instances, i.e. with arrays, so I ended up writing yet another one. I don't use the text from Type.Name or similar except to get the plain name of the types, because I don't know if the format is guaranteed to be the same across different .Net versions or with other implementations of the libraries (I assume it isn't).

    /// <summary>
    /// For the given type, returns its representation in C# code.
    /// </summary>
    /// <param name="type">The type.</param>
    /// <param name="genericArgs">Used internally, ignore.</param>
    /// <param name="arrayBrackets">Used internally, ignore.</param>
    /// <returns>The representation of the type in C# code.</returns>
    
    public static string GetTypeCSharpRepresentation(Type type, Stack<Type> genericArgs = null, StringBuilder arrayBrackets = null)
    {
        StringBuilder code = new StringBuilder();
        Type declaringType = type.DeclaringType;
    
        bool arrayBracketsWasNull = arrayBrackets == null;
    
        if (genericArgs == null)
            genericArgs = new Stack<Type>(type.GetGenericArguments());
    
    
        int currentTypeGenericArgsCount = genericArgs.Count;
        if (declaringType != null)
            currentTypeGenericArgsCount -= declaringType.GetGenericArguments().Length;
    
        Type[] currentTypeGenericArgs = new Type[currentTypeGenericArgsCount];
        for (int i = currentTypeGenericArgsCount - 1; i >= 0; i--)
            currentTypeGenericArgs[i] = genericArgs.Pop();
    
    
        if (declaringType != null)
            code.Append(GetTypeCSharpRepresentation(declaringType, genericArgs)).Append('.');
    
    
        if (type.IsArray)
        {
            if (arrayBrackets == null)
                arrayBrackets = new StringBuilder();
    
            arrayBrackets.Append('[');
            arrayBrackets.Append(',', type.GetArrayRank() - 1);
            arrayBrackets.Append(']');
    
            Type elementType = type.GetElementType();
            code.Insert(0, GetTypeCSharpRepresentation(elementType, arrayBrackets : arrayBrackets));
        }
        else
        {
            code.Append(new string(type.Name.TakeWhile(c => char.IsLetterOrDigit(c) || c == '_').ToArray()));
    
            if (currentTypeGenericArgsCount > 0)
            {
                code.Append('<');
                for (int i = 0;  i < currentTypeGenericArgsCount;  i++)
                {
                    code.Append(GetTypeCSharpRepresentation(currentTypeGenericArgs[i]));
                    if (i < currentTypeGenericArgsCount - 1)
                        code.Append(',');
                }
                code.Append('>');
            }
    
            if (declaringType == null  &&  !string.IsNullOrEmpty(type.Namespace))
            {
                code.Insert(0, '.').Insert(0, type.Namespace);
            }
        }
    
    
        if (arrayBracketsWasNull  &&  arrayBrackets != null)
            code.Append(arrayBrackets.ToString());
    
    
        return code.ToString();
    }
    

    I have tested it with crazy types like this, and so far it has worked perfectly:

    class C
    {
        public class D<D1, D2>
        {
            public class E
            {
                public class K<R1, R2, R3>
                {
                    public class P<P1>
                    {
                        public struct Q
                        {
                        }
                    }
                }
            }
        }
    }
    
    type = typeof(List<Dictionary<string[], C.D<byte, short[,]>.E.K<List<int>[,][], Action<List<long[][][,]>[], double[][,]>, float>.P<string>.Q>>[][,][,,,][][,,]);
    
    // Returns "System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String[],Test.Program.C.D<System.Byte,System.Int16[,]>.E.K<System.Collections.Generic.List<System.Int32>[,][],System.Action<System.Collections.Generic.List<System.Int64[][][,]>[],System.Double[][,]>,System.Single>.P<System.String>.Q>>[][,][,,,][][,,]":
    GetTypeCSharpRepresentation(type);
    

    There may still be some gotchas I didn't think about, but there's a known one: to retrieve the names, I only get characters that meet the condition char.IsLetterOrDigit(c) || c == '_' until one that doesn't is found, so any names of types that use allowed characters that don't meet the condition will fail.

    0 讨论(0)
  • 2020-12-05 23:40

    Another way to get a nice type name by using an extension:

    typeof(Dictionary<string, Dictionary<decimal, List<int>>>).CSharpName();
    // output is: 
    // Dictionary<String, Dictionary<Decimal, List<Int32>>>
    

    The Extension Code:

    public static class TypeExtensions
    {
       public static string CSharpName(this Type type)
       {
           string typeName = type.Name;
    
           if (type.IsGenericType)
           {
               var genArgs = type.GetGenericArguments();
    
               if (genArgs.Count() > 0)
               {
                   typeName = typeName.Substring(0, typeName.Length - 2);
    
                   string args = "";
    
                   foreach (var argType in genArgs)
                   {
                       string argName = argType.Name;
    
                       if (argType.IsGenericType)
                           argName = argType.CSharpName();
    
                       args += argName + ", ";
                   }
    
                   typeName = string.Format("{0}<{1}>", typeName, args.Substring(0, args.Length - 2));
               }
           }
    
           return typeName;
       }        
    }
    
    0 讨论(0)
  • 2020-12-05 23:43

    An improvement on Adam Sills's answer that works with non-generic nested types, and generic type definitions:

    public class TypeNameStringExtensions
    {
        public static string GetCSharpRepresentation(Type t)
        {
            return GetCSharpRepresentation(t, new Queue<Type>(t.GetGenericArguments()));
        }
        static string GetCSharpRepresentation(Type t, Queue<Type> availableArguments)
        {
            string value = t.Name;
            if (t.IsGenericParameter)
            {
                return value;
            }
            if (t.DeclaringType != null)
            {
                // This is a nested type, build the parent type first
                value = GetCSharpRepresentation(t.DeclaringType, availableArguments) + "+" + value;
            }
            if (t.IsGenericType)
            {
                value = value.Split('`')[0];
    
                // Build the type arguments (if any)
                string argString = "";
                var thisTypeArgs = t.GetGenericArguments();
                for (int i = 0; i < thisTypeArgs.Length && availableArguments.Count > 0; i++)
                {
                    if (i != 0) argString += ", ";
    
                    argString += GetCSharpRepresentation(availableArguments.Dequeue());
                }
    
                // If there are type arguments, add them with < >
                if (argString.Length > 0)
                {
                    value += "<" + argString + ">";
                }
            }
    
            return value;
        }
    
        [TestCase(typeof(List<string>), "List<String>")]
        [TestCase(typeof(List<Dictionary<int, string>>), "List<Dictionary<Int32, String>>")]
        [TestCase(typeof(Stupid<int>.Stupider<int>), "Stupid<Int32>+Stupider<Int32>")]
        [TestCase(typeof(Dictionary<int, string>.KeyCollection), "Dictionary<Int32, String>+KeyCollection")]
        [TestCase(typeof(Nullable<Point>), "Nullable<Point>")]
        [TestCase(typeof(Point?), "Nullable<Point>")]
        [TestCase(typeof(TypeNameStringExtensions), "TypeNameStringExtensions")]
        [TestCase(typeof(Another), "TypeNameStringExtensions+Another")]
        [TestCase(typeof(G<>), "TypeNameStringExtensions+G<T>")]
        [TestCase(typeof(G<string>), "TypeNameStringExtensions+G<String>")]
        [TestCase(typeof(G<Another>), "TypeNameStringExtensions+G<TypeNameStringExtensions+Another>")]
        [TestCase(typeof(H<,>), "TypeNameStringExtensions+H<T1, T2>")]
        [TestCase(typeof(H<string, Another>), "TypeNameStringExtensions+H<String, TypeNameStringExtensions+Another>")]
        [TestCase(typeof(Another.I<>), "TypeNameStringExtensions+Another+I<T3>")]
        [TestCase(typeof(Another.I<int>), "TypeNameStringExtensions+Another+I<Int32>")]
        [TestCase(typeof(G<>.Nested), "TypeNameStringExtensions+G<T>+Nested")]
        [TestCase(typeof(G<string>.Nested), "TypeNameStringExtensions+G<String>+Nested")]
        [TestCase(typeof(A<>.C<>), "TypeNameStringExtensions+A<B>+C<D>")]
        [TestCase(typeof(A<int>.C<string>), "TypeNameStringExtensions+A<Int32>+C<String>")]
        public void GetCSharpRepresentation_matches(Type type, string expected)
        {
            string actual = GetCSharpRepresentation(type);
            Assert.AreEqual(expected, actual);
        }
    
        public class G<T>
        {
            public class Nested { }
        }
    
        public class A<B>
        {
            public class C<D> { }
        }
    
        public class H<T1, T2> { }
    
        public class Another
        {
            public class I<T3> { }
        }
    }
    
    public class Stupid<T1>
    {
        public class Stupider<T2>
        {
        }
    }
    

    I also chose to forgo his trimArgCount, as I can't see when that would be useful, and to use a Queue<Type> since that was the intent (pulling items off the front while they exist).

    0 讨论(0)
提交回复
热议问题