C# declaration of generic type

后端 未结 5 924
不知归路
不知归路 2021-01-21 06:09

Is it possible to get the \"c# name\" of a type obtained with reflexion like:

System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Cultur         


        
5条回答
  •  南方客
    南方客 (楼主)
    2021-01-21 06:50

    You can examine the type to find out if it is nested or not and if it is generic constructed or a primitive type. In particular I've realized that nested generic types lists its generic arguments at the end of the type name string. That is mentioned in documentation Type.GetType Method (String).

    I have partially solved the task and my solution passes the following tests.

            [TestMethod]
            public void Tests()
            {
    
                Assert.AreEqual("int",typeof(int).ToCSharpMethodReturnTypeName());
                Assert.AreEqual("int[]",typeof(int[]).ToCSharpMethodReturnTypeName());
                Assert.AreEqual("int?", typeof(int?).ToCSharpMethodReturnTypeName());
    
                Assert.AreEqual("UnitTypeFriendlyNamesExtensionsTest.Inner2>.SubInner,object>", typeof(Inner2>.SubInner,object>).ToCSharpMethodReturnTypeName());
    
                Assert.ThrowsException(()=> typeof(Inner2>.SubInner, object>).DeclaringType.ToCSharpMethodReturnTypeName());
    
                var t = typeof(TestGenericReturnType);
                var m = t.GetMethod("GetService");
                var tt = m.ReturnType;
    
                Assert.AreEqual("DateTime",tt.ToCSharpMethodReturnTypeName());
    
                Assert.AreEqual("IDictionary",typeof(IDictionary).ToCSharpMethodReturnTypeName());
                Assert.AreEqual("IList",typeof(IList).ToCSharpMethodReturnTypeName());
                Assert.AreEqual("UnitTypeFriendlyNamesExtensionsTest.Astruc?",typeof(Astruc?).ToCSharpMethodReturnTypeName());
                Assert.AreEqual("UnitTypeFriendlyNamesExtensionsTest.Inner", typeof(Inner).ToCSharpMethodReturnTypeName());
            }
    

    The extensions class handles nested and generic constructed types as well as primitive types.

    public static class TypeFriendlyNamesExtensions
    {
        private static readonly Dictionary TypeToFriendlyName = new Dictionary
        {
            {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 ToCSharpMethodReturnTypeName(this Type type)
        {
            var sb = new StringWriter();
            ToCSharpNameEntry2(sb, type);
            return sb.ToString();
        }
    
        private static void ToCSharpNameEntry2(TextWriter sb, Type type0)
        {
            var list = GetContainingTypeList(type0);
            var usedGenericArgumentCounter = 0;
    
            HandleNestedLevel(sb, list, 0, ref usedGenericArgumentCounter);
            foreach (var ix in Enumerable.Range(1, list.Count - 1))
            {
                sb.Write(".");
                HandleNestedLevel(sb, list, ix, ref usedGenericArgumentCounter);
            }
        }
    
        private static void HandleNestedLevel(TextWriter sb, IReadOnlyList list, int ix,
            ref int usedGenericArgumentCounter)
        {
            var type = list[ix];
            if (TypeToFriendlyName.TryGetValue(type, out string fname))
            {
                sb.Write(fname);
                return;
            }
            if (type.IsGenericParameter)
            {
                sb.Write(type.Name);
                return;
            }
            if (type.IsArray)
            {
                ToCSharpNameEntry2(sb, type.GetElementType());
                sb.Write("[]");
                return;
            }
    
            if (type.IsGenericType)
            {
                var innermostType = list[list.Count - 1];
                var args = list[list.Count - 1].GenericTypeArguments;
                if (!type.IsConstructedGenericType)
                {
                    if (innermostType.IsConstructedGenericType)
                    {
                        var sname = GetSname(type);
                        sb.Write(sname);
                        sb.Write("<");
                        ToCSharpNameEntry2(sb, args[usedGenericArgumentCounter++]);
                        var loopCounter = ((TypeInfo) type).GenericTypeParameters.Length;
                        while (0 < --loopCounter)
                        {
                            sb.Write(",");
                            ToCSharpNameEntry2(sb, args[usedGenericArgumentCounter++]);
                        }
                        sb.Write(">");
                        return;
                    }
                    throw new NotImplementedException();
                }
                if (typeof(Nullable<>) == type.GetGenericTypeDefinition())
                {
                    ToCSharpNameEntry2(sb, args[0]);
                    sb.Write("?");
                    return;
                }
                var cname = GetSname(type);
                sb.Write(cname);
                sb.Write("<");
                ToCSharpNameEntry2(sb, args[usedGenericArgumentCounter++]);
                while (usedGenericArgumentCounter < args.Length)
                {
                    sb.Write(",");
                    ToCSharpNameEntry2(sb, args[usedGenericArgumentCounter++]);
                }
                sb.Write(">");
                return;
            }
            if (type.IsPointer)
            {
                ToCSharpNameEntry2(sb, type);
                sb.Write("*");
                return;
            }
            sb.Write(type.Name);
        }
    
        private static string GetSname(Type type)
        {
            var name = type.Name;
            return name.Substring(0, name.IndexOf('`'));
        }
    
        private static List GetContainingTypeList(Type type)
        {
            var list = new List {type};
            var t = type;
            while (t.IsNested)
            {
                t = t.DeclaringType;
                list.Insert(0, t);
            }
            return list;
        }
    
        private static void ToCSharpNameEntry(StringBuilder sb, Type type)
        {
            var genericTypeArguments = type.GenericTypeArguments;
            var usedGenericTypeCounter = 0;
            ToCSharpNameRecursive(sb, type, genericTypeArguments, ref usedGenericTypeCounter);
        }
    
        private static void ToCSharpNameRecursive(StringBuilder sb, Type type, IReadOnlyList genericTypeArguments,
            ref int genericTypesCounter)
        {
            if (TypeToFriendlyName.TryGetValue(type, out string res))
            {
                sb.Append(res);
                return;
            }
    
            HandleNonPriminiveTypes(sb, type, genericTypeArguments, ref genericTypesCounter);
        }
    
        private static void HandleNonPriminiveTypes(StringBuilder sb, Type type, IReadOnlyList typeGenericTypeArguments,
            ref int genericTypesCounter)
        {
            var list = new List();
            var t = type;
            list.Add(t);
            while (t.IsNested)
            {
                t = t.DeclaringType;
                list.Add(t);
            }
            list.Reverse();
            foreach (var type1 in list)
            {
                HandleNestedLevel(sb, type1, typeGenericTypeArguments, ref genericTypesCounter);
                sb.Append(".");
            }
            sb.Length -= 1;
        }
    
        private static void HandleNestedLevel(StringBuilder sb, Type type, IReadOnlyList typeGenericTypeArguments,
            ref int genericTypesCounter)
        {
            var name = type.Name;
            if (type.IsGenericType)
            {
                var info = type.GetTypeInfo();
                var def = info.GetGenericTypeDefinition();
                var psLength = info.IsConstructedGenericType
                    ? info.GenericTypeArguments.Length
                    : info.GenericTypeParameters.Length;
                if (typeof(Nullable<>) == def)
                {
                    var type1 = typeGenericTypeArguments[genericTypesCounter++];
                    ToCSharpNameEntry(sb, type1);
                    sb.Append("?");
                    return;
                }
    
                var n = name.Substring(0, name.IndexOf("`", StringComparison.InvariantCultureIgnoreCase));
    
                sb.Append(n);
                sb.Append("<");
                for (var i = 0; i < psLength; i++)
                {
                    ToCSharpNameEntry(sb, typeGenericTypeArguments[genericTypesCounter++]);
                    sb.Append(",");
                }
                sb.Length -= 1;
                sb.Append(">");
                return;
            }
            if (type.IsArray)
            {
                var type1 = type.GetElementType();
                ToCSharpNameEntry(sb, type1);
                sb.Append("[]");
                return;
            }
            sb.Append(name);
        }
    }
    

    I use the extension method while composing the source text to be compiled by Roslyn

提交回复
热议问题