C# Get Generic Type Name

前端 未结 9 1686
旧时难觅i
旧时难觅i 2020-11-30 06:09

I need some way to get the Name of a Type, when type.IsGenericType = true.

    Type t = typeof(List);
    MessageBox.         


        
相关标签:
9条回答
  • 2020-11-30 07:11

    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. ;-)

    0 讨论(0)
  • 2020-11-30 07:11

    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());
        }
    }
    
    0 讨论(0)
  • 2020-11-30 07:11

    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

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