C#: “Pretty” type name function?

耗尽温柔 提交于 2019-12-05 12:55:18

问题


The name properties of System.Type class return a strange result in case of generic types. Is there a way to get the type name in a format closer to the way I specified it? Example: typeof(List<string>).OriginalName == "List<string>"


回答1:


The problem with "pretty" names is they are different depending on the language you are using. Imagine the surprise of a VB.NET developer if OriginalName returned C# syntax.

However, it's pretty fairly easy to make this yourself:

private static string PrettyName(Type type)
{
    if (type.GetGenericArguments().Length == 0)
    {
        return type.Name;
    }
    var genericArguments = type.GetGenericArguments();
    var typeDefeninition = type.Name;
    var unmangledName = typeDefeninition.Substring(0, typeDefeninition.IndexOf("`"));
    return unmangledName + "<" + String.Join(",", genericArguments.Select(PrettyName)) + ">";
}

This will recursively resolve the unmanaged name, so that if you have something like Dictionary<string, IList<string>> it should still work.




回答2:


I used CodeDomProvider to convert to c#:

    public static string GetOriginalName(this Type type)
    {
        string TypeName = type.FullName.Replace(type.Namespace + ".", "");//Removing the namespace

        var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); //You can also use "VisualBasic"
        var reference = new System.CodeDom.CodeTypeReference(TypeName);

        return provider.GetTypeOutput(reference);
    }



回答3:


You have to write this yourself. Keep in mind that Type.Name etc. are invoking methods that live in the CLR and can be invoked from multiple languages. This is why they don't come back looking like C# or VB or the language the caller was coded in, but instead looking like the CLR representation.

Note further that string and what not are aliases for CLR types like System.String. Again, this plays a role in the formatting that you see.

It's not hard to do using reflection, but I question the value of it.




回答4:


Like Harold Hoyer's answer but including nullables and a few more built-in types:

/// <summary>
/// Get full type name with full namespace names
/// </summary>
/// <param name="type">
/// The type to get the C# name for (may be a generic type or a nullable type).
/// </param>
/// <returns>
/// Full type name, fully qualified namespaces
/// </returns>
public static string CSharpName(this Type type)
{
    Type nullableType = Nullable.GetUnderlyingType(type);
    string nullableText;
    if (nullableType != null)
    {
        type = nullableType;
        nullableText = "?";
    }
    else
    {
        nullableText = string.Empty;
    }

    if (type.IsGenericType)
    {
        return string.Format(
            "{0}<{1}>{2}", 
            type.Name.Substring(0, type.Name.IndexOf('`')), 
            string.Join(", ", type.GetGenericArguments().Select(ga => ga.CSharpName())), 
            nullableText);
    }

    switch (type.Name)
    {
        case "String":
            return "string";
        case "Int32":
            return "int" + nullableText;
        case "Decimal":
            return "decimal" + nullableText;
        case "Object":
            return "object" + nullableText;
        case "Void":
            return "void" + nullableText;
        default:
            return (string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName) + nullableText;
    }
}



回答5:


Here is my implementation. It was created to describe methods, so it handles the ref and out keywords.

private static Dictionary<Type, string> shorthandMap = new Dictionary<Type, string>
{
    { typeof(Boolean), "bool" },
    { typeof(Byte), "byte" },
    { typeof(Char), "char" },
    { typeof(Decimal), "decimal" },
    { typeof(Double), "double" },
    { typeof(Single), "float" },
    { typeof(Int32), "int" },
    { typeof(Int64), "long" },
    { typeof(SByte), "sbyte" },
    { typeof(Int16), "short" },
    { typeof(String), "string" },
    { typeof(UInt32), "uint" },
    { typeof(UInt64), "ulong" },
    { typeof(UInt16), "ushort" },
};

private static string CSharpTypeName(Type type, bool isOut = false)
{
    if (type.IsByRef)
    {
        return String.Format("{0} {1}", isOut ? "out" : "ref", CSharpTypeName(type.GetElementType()));
    }
    if (type.IsGenericType)
    {
        if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            return String.Format("{0}?", CSharpTypeName(Nullable.GetUnderlyingType(type)));
        }
        else
        {
            return String.Format("{0}<{1}>", type.Name.Split('`')[0],
                String.Join(", ", type.GenericTypeArguments.Select(a => CSharpTypeName(a)).ToArray()));
        }
    }
    if (type.IsArray)
    {
        return String.Format("{0}[]", CSharpTypeName(type.GetElementType()));
    }

    return shorthandMap.ContainsKey(type) ? shorthandMap[type] : type.Name;
}

The calling code looks like this:

string line = String.Format("{0}.{1}({2})",
    method.DeclaringType.Name,
    method.Name,
    String.Join(", ", method.GetParameters().Select(p => CSharpTypeName(p.ParameterType, p.IsOut) + " " + p.Name).ToArray()));

Where method is a MethodInfo instance.

One note: I didn't have a need to describe any multi-dimensional array types, so I didn't bother implementing description for that, but it would be fairly easy to add by calling type.GetArrayRank().




回答6:


A minimal work solution that leverages CodeDomProvider is to control how the CodeTypeReference instance is built in the first place. There are special cases only for generic types and multi-rank arrays, so we only have to care about those:

static CodeTypeReference CreateTypeReference(Type type)
{
    var typeName = (type.IsPrimitive || type == typeof(string)) ? type.FullName : type.Name;
    var reference = new CodeTypeReference(typeName);
    if (type.IsArray)
    {
        reference.ArrayElementType = CreateTypeReference(type.GetElementType());
        reference.ArrayRank = type.GetArrayRank();
    }

    if (type.IsGenericType)
    {
        foreach (var argument in type.GetGenericArguments())
        {
            reference.TypeArguments.Add(CreateTypeReference(argument));
        }
    }
    return reference;
}

Using this modified factory method, it is then possible to use the appropriate code provider to get pretty typing, like so:

using (var provider = new CSharpCodeProvider())
{
    var reference = CreateTypeReference(typeof(IObservable<IEnumerable<Tuple<int?, string>>>[,]));
    var output = provider.GetTypeOutput(reference);
    Console.WriteLine(output);
}

The above code returns IObservable<IEnumerable<Tuple<Nullable<int>, string>>>[,]. The only special case that is not handled well are Nullable types but this is really more a fault of the CodeDomProvider than anything else.




回答7:


as in your example you can expect the type so you can try that

public class test<T> where T : class
{
    public List<String> tt
    {
        get;
        set;
    }
}
 ///////////////////////////
 test<List<String>> tt = new  test<List<String>>();
if(tt.GetType().FullName.Contains(TypeOf(List<String>).FullName))
{
   //do something
}
else
{
    //do something else
}



回答8:


I understand that you want to compare types.
The best way to do this is...
myVar is List<string> or
myVar.GetType() == myOtherVar.GetType()

If you don't need this... please disregard my answer.




回答9:


I understood, that I have to write this myself. Here is my solution (it is actually a bit more than asked for). It is, probably, helpfull.

using System.Reflection;
using HWClassLibrary.Debug;
using System.Collections.Generic;
using System.Linq;
using System;

namespace HWClassLibrary.Helper
{
    public static class TypeNameExtender
    {
        private static IEnumerable<Type> _referencedTypesCache;

        public static void OnModuleLoaded() { _referencedTypesCache = null; }

        public static string PrettyName(this Type type)
        {
            if(type == typeof(int))
                return "int";
            if(type == typeof(string))
                return "string";

            var result = PrettyTypeName(type);
            if(type.IsGenericType)
                result = result + PrettyNameForGeneric(type.GetGenericArguments());
            return result;
        }

        private static string PrettyTypeName(Type type)
        {
            var result = type.Name;
            if(type.IsGenericType)
                result = result.Remove(result.IndexOf('`'));

            if (type.IsNested && !type.IsGenericParameter)
                return type.DeclaringType.PrettyName() + "." + result;

            if(type.Namespace == null)
                return result;

            var conflictingTypes = ReferencedTypes
                .Where(definedType => definedType.Name == type.Name && definedType.Namespace != type.Namespace)
                .ToArray();

            var namespaceParts = type.Namespace.Split('.').Reverse().ToArray();
            var namespacePart = "";
            for(var i = 0; i < namespaceParts.Length && conflictingTypes.Length > 0; i++)
            {
                namespacePart = namespaceParts[i] + "." + namespacePart;
                conflictingTypes = conflictingTypes
                    .Where(conflictingType => (conflictingType.Namespace + ".").EndsWith(namespacePart))
                    .ToArray();
            }

            return namespacePart + result;
        }

        private static IEnumerable<Type> ReferencedTypes
        {
            get
            {
                if(_referencedTypesCache == null)
                    _referencedTypesCache = Assembly.GetEntryAssembly().GetReferencedTypes();
                return _referencedTypesCache;
            }
        }

        private static string PrettyNameForGeneric(Type[] types)
        {
            var result = "";
            var delim = "<";
            foreach(var t in types)
            {
                result += delim;
                delim = ",";
                result += t.PrettyName();
            }
            return result + ">";
        }
    }
}



回答10:


First: Kudos to Navid for wheel reinvention avoidance. I would upvote if I could.

Here are a few points to add, if anyone goes down this path (at least for VS10/.Net 4):
* Try using one of the variants of CodeTypeReference with Type arguments. This avoids a number of pitfalls of using string type names (such as trailing &) and means you get back bool instead of System.Boolean etc. You do get back the full namespace for a lot of types like this but you can always get rid of the namespace part later.
* Simple Nullables tend to come back in the form System.Nullable<int> rather than int? - You can convert to the nicer syntax with a regex on the answer such as: const string NullablePattern = @"System.Nullable<(?<nulledType>[\w\.]+)>"; const string NullableReplacement = @"${nulledType}?"; answer = Regex.Replace(answer, NullablePattern, NullableReplacement);
* The Type of a method argument like out T? gives a very opaque string. If anyone has an elegant way of dealing with things like this I would love to know about it.

Hopefully this all becomes very easy with Roslyn.




回答11:


I do it like this ..

public static class TypeExtensions {
    public static String GetName(this Type t) {
        String readable;

#if PREVENT_RECURSION
        if(m_names.TryGetValue(t, out readable)) {
            return readable;
        }
#endif

        var tArgs = t.IsGenericType ? t.GetGenericArguments() : Type.EmptyTypes;
        var name = t.Name;
        var format = Regex.Replace(name, @"`\d+.*", "")+(t.IsGenericType ? "<?>" : "");
        var names = tArgs.Select(x => x.IsGenericParameter ? "" : GetName(x));
        readable=String.Join(String.Join(",", names), format.Split('?'));

#if PREVENT_RECURSION
        m_names.Add(t, readable);
#endif

        return readable;
    }

    static readonly Dictionary<Type, String> m_names = new Dictionary<Type, String> { };
}

where PREVENT_RECURSION should be defined true if you don't want the type in generic type arguments be resolved recursively every time, just take from the cache dictionary; leave it undefined of false if you don't care that.



来源:https://stackoverflow.com/questions/6402864/c-pretty-type-name-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!