C# declaration of generic type

后端 未结 5 921
不知归路
不知归路 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:41

    ...without splitting strings?

    AFAIK, no.

    0 讨论(0)
  • 2021-01-21 06:43

    You need to parse a string like:

    t`x[[a(,b,c,d)]]
    

    where t is actual type; x - number of generic parameters; a,b,c,d,etc - generic aguments

    0 讨论(0)
  • 2021-01-21 06:47

    Yes, you can do it without resorting to splitting, parsing or manipulating strings by using CodeDom and CSharpCodeProvider:

    using CodeDom;
    using Microsoft.CSharp;
    
    // ...
    
    Type yourType = typeof(List<string>);  // for example
    
    using (var provider = new CSharpCodeProvider())
    {
        var typeRef = new CodeTypeReference(yourType);
        Console.WriteLine(provider.GetTypeOutput(typeRef));
    }
    

    (You may need to do some additional string manipulation to remove the namespace prefix. For example, if you want the output to be List<string> rather than System.Collections.Generic.List<string>.)

    0 讨论(0)
  • 2021-01-21 06:49

    Not directly, but you can examine the type itself to figure it out.

    public static string TypeName(Type t) {
        if (!t.IsGenericType) return t.Name;
    
        StringBuilder ret = new StringBuilder();
        ret.Append(t.Name).Append("<");
    
        bool first = true;
        foreach(var arg in t.GetGenericArguments()) {
            if (!first) ret.Append(", ");
            first = false;
    
            ret.Append(TypeName(arg));
        }
    
        ret.Append(">");
        return ret.ToString();
    }
    
    0 讨论(0)
  • 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<string,IDictionary<string,object>>.SubInner<int,List<byte[]>,object>", typeof(Inner2<string,IDictionary<string,object>>.SubInner<int,List<byte[]>,object>).ToCSharpMethodReturnTypeName());
    
                Assert.ThrowsException<NotImplementedException>(()=> typeof(Inner2<string, IDictionary<string, object>>.SubInner<int, List<byte[]>, object>).DeclaringType.ToCSharpMethodReturnTypeName());
    
                var t = typeof(TestGenericReturnType<DateTime>);
                var m = t.GetMethod("GetService");
                var tt = m.ReturnType;
    
                Assert.AreEqual("DateTime",tt.ToCSharpMethodReturnTypeName());
    
                Assert.AreEqual("IDictionary<string,object>",typeof(IDictionary<string,object>).ToCSharpMethodReturnTypeName());
                Assert.AreEqual("IList<int[]>",typeof(IList<int[]>).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<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 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<Type> 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<Type> GetContainingTypeList(Type type)
        {
            var list = new List<Type> {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<Type> 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<Type> typeGenericTypeArguments,
            ref int genericTypesCounter)
        {
            var list = new List<Type>();
            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<Type> 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

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