C#:“漂亮”类型 名称 功能?

发布于 2024-11-16 04:28:44 字数 151 浏览 4 评论 0 原文

对于泛型类型,System.Type 类的名称属性会返回一个奇怪的结果。有没有办法以更接近我指定的方式获取类型名称? 示例:typeof(List).OriginalName == "List"

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>"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

微凉徒眸意 2024-11-23 04:28:44

“漂亮”名称的问题在于它们根据您使用的语言而有所不同。想象一下,如果 OriginalName 返回 C# 语法,VB.NET 开发人员会感到多么惊讶。

但是,您自己制作它相当容易:

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

这将递归地解析非托管名称,因此,如果您有类似 Dictionary> 的内容,它应该仍然可以工作。

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 typeDefinition = type.Name;
    var unmangledName = typeDefinition.Substring(0, typeDefinition.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.

或十年 2024-11-23 04:28:44

我使用 CodeDomProvider 转换为 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);
    }

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);
    }
悟红尘 2024-11-23 04:28:44

就像 Harold Hoyer 的答案,但包括可空值和一些其他内置类型:

/// <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;
    }
}

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;
    }
}
稳稳的幸福 2024-11-23 04:28:44

这是我的实现。它是为了描述方法而创建的,因此它可以处理 refout 关键字。

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;
}

调用代码如下所示:

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

其中 methodMethodInfo 实例。

需要注意的是:我不需要描述任何多维数组类型,因此我没有费心实现描述,但通过调用 type.GetArrayRank() 来添加会相当容易代码>.

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

审判长 2024-11-23 04:28:44

利用 CodeDomProvider 的最小工作解决方案是首先控制 CodeTypeReference 实例的构建方式。仅针对泛型类型和多等级数组有特殊情况,因此我们只需要关心这些:

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

上面的代码返回 IObservable, string>>>[,]。唯一处理不好的特殊情况是 Nullable 类型,但这实际上更多是 CodeDomProvider 的错误。

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.

我是有多爱你 2024-11-23 04:28:44

你必须自己写这个。请记住,Type.Name 等正在调用 CLR 中的方法,并且可以从多种语言调用。这就是为什么它们看起来不像 C# 或 VB 或调用者编码所用的语言,而是看起来像 CLR 表示形式。

进一步注意,string 以及其他类型的别名是诸如 System.String 之类的 CLR 类型的别名。同样,这会影响您所看到的格式。

使用反射并不难,但我质疑它的价值。

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.

Spring初心 2024-11-23 04:28:44

第一:感谢纳维德避免重新发明轮子。如果可以的话我会投票。

如果有人走这条路(至少对于 VS10/.Net 4),这里有几点需要补充:
* 尝试将 CodeTypeReference 的变体之一与类型参数一起使用。这避免了使用字符串类型名称(例如尾随 &)的许多陷阱,并且意味着您将返回 bool 而不是 System.Boolean 等。像这样的许多类型的完整命名空间,但您可以稍后删除命名空间部分。
* 简单的 Nullables 往往以 System.Nullable 的形式返回,而不是 int? - 您可以使用答案上的正则表达式转换为更好的语法,例如:<代码>
const string NullablePattern = @"System.Nullable<(?[\w\.]+)>";
const string NullableReplacement = @"${nulledType}?";
答案 = Regex.Replace(answer, NullablePattern, NullableReplacement);
* 像 out T? 这样的方法参数的类型给出了一个非常不透明的字符串。如果有人有一种优雅的方式来处理这样的事情,我很想知道。

希望这一切在罗斯林的帮助下变得非常容易。

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.

吃不饱 2024-11-23 04:28:44

结合了一些答案,添加了对嵌套类型和数组等级的支持,并将其转变为具有缓存解析名称的扩展方法。

/// <summary>
/// Extension methods for <see cref="Type"/>.
/// </summary>
public static class TypeExtensions
{
    /// <summary>
    /// Dictionary of type names.
    /// </summary>
    private static readonly ConcurrentDictionary<Type, string> typeNames;

    /// <summary>
    /// Initializes static members of the <see cref="TypeExtensions"/> class.
    /// </summary>
    static TypeExtensions()
    {
        typeNames = new ConcurrentDictionary<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"
                        };
    }

    /// <summary>
    /// Gets the type name with generics and array ranks resolved.
    /// </summary>
    /// <param name="type">
    /// The type whose name to resolve.
    /// </param>
    /// <returns>
    /// The resolved type name.
    /// </returns>
    public static string ToCSTypeName(this Type type)
    {
        return typeNames.GetOrAdd(type, GetPrettyTypeName);
    }

    /// <summary>
    /// Gets the type name as it would be written in C#
    /// </summary>
    /// <param name="type">
    /// The type whose name is to be written.
    /// </param>
    /// <returns>
    /// The type name as it is written in C#
    /// </returns>
    private static string GetPrettyTypeName(Type type)
    {
        var typeNamespace = type.DeclaringType != null ? ToCSTypeName(type.DeclaringType) : type.Namespace;
        if (type.IsGenericType)
        {
            if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return $"{ToCSTypeName(Nullable.GetUnderlyingType(type))}?";
            }

            var typeList = string.Join(", ", type.GenericTypeArguments.Select(ToCSTypeName).ToArray());
            var typeName = type.Name.Split('`')[0];

            return $"{typeNamespace}.{typeName}<{typeList}>";
        }

        if (type.IsArray)
        {
            var arrayRank = string.Empty.PadLeft(type.GetArrayRank() - 1, ',');
            var elementType = ToCSTypeName(type.GetElementType());
            return $"{elementType}[{arrayRank}]";
        }

        return $"{typeNamespace}.{type.Name}";
    }
}

Combined a few answers, added support for nested types and array ranks, and turned it into an extension method with cached resolved names.

/// <summary>
/// Extension methods for <see cref="Type"/>.
/// </summary>
public static class TypeExtensions
{
    /// <summary>
    /// Dictionary of type names.
    /// </summary>
    private static readonly ConcurrentDictionary<Type, string> typeNames;

    /// <summary>
    /// Initializes static members of the <see cref="TypeExtensions"/> class.
    /// </summary>
    static TypeExtensions()
    {
        typeNames = new ConcurrentDictionary<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"
                        };
    }

    /// <summary>
    /// Gets the type name with generics and array ranks resolved.
    /// </summary>
    /// <param name="type">
    /// The type whose name to resolve.
    /// </param>
    /// <returns>
    /// The resolved type name.
    /// </returns>
    public static string ToCSTypeName(this Type type)
    {
        return typeNames.GetOrAdd(type, GetPrettyTypeName);
    }

    /// <summary>
    /// Gets the type name as it would be written in C#
    /// </summary>
    /// <param name="type">
    /// The type whose name is to be written.
    /// </param>
    /// <returns>
    /// The type name as it is written in C#
    /// </returns>
    private static string GetPrettyTypeName(Type type)
    {
        var typeNamespace = type.DeclaringType != null ? ToCSTypeName(type.DeclaringType) : type.Namespace;
        if (type.IsGenericType)
        {
            if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return 
quot;{ToCSTypeName(Nullable.GetUnderlyingType(type))}?";
            }

            var typeList = string.Join(", ", type.GenericTypeArguments.Select(ToCSTypeName).ToArray());
            var typeName = type.Name.Split('`')[0];

            return 
quot;{typeNamespace}.{typeName}<{typeList}>";
        }

        if (type.IsArray)
        {
            var arrayRank = string.Empty.PadLeft(type.GetArrayRank() - 1, ',');
            var elementType = ToCSTypeName(type.GetElementType());
            return 
quot;{elementType}[{arrayRank}]";
        }

        return 
quot;{typeNamespace}.{type.Name}";
    }
}
旧瑾黎汐 2024-11-23 04:28:44

正如您的示例中所示,您可以期待该类型,因此您可以尝试一下

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
}

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
}
凉城 2024-11-23 04:28:44

我知道您想比较类型。
做到这一点的最好方法是...
myVar 是 List
myVar.GetType() == myOtherVar.GetType()

如果您不需要这个...请忽略我的回答。

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.

榆西 2024-11-23 04:28:44

我明白了,我必须自己写这个。这是我的解决方案(实际上比要求的要多一些)。这可能是有帮助的。

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 + ">";
        }
    }
}

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 + ">";
        }
    }
}
影子是时光的心 2024-11-23 04:28:44

我这样做..

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> { };
}

如果您不希望每次都递归地解析泛型类型参数中的类型,则应定义 PREVENT_RECURSION true ,只需从缓存中获取字典;如果你不关心的话,请将其保留为未定义的 false 。

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.

一袭水袖舞倾城 2024-11-23 04:28:44
public static string pretty_name( Type type, int recursion_level = -1, bool expand_nullable = false )
{
    if( type.IsArray )
    {
        return $"{pretty_name( type.GetElementType(), recursion_level, expand_nullable )}[]";
    }

    if( type.IsGenericType )
    {
        // find generic type name
        var gen_type_name = type.GetGenericTypeDefinition().Name;
        var index = gen_type_name.IndexOf( '`' );
        if( index != -1 )
            gen_type_name = gen_type_name.Substring( 0, index );

        // retrieve generic type aguments
        var arg_names = new List<string>();
        var gen_type_args = type.GetGenericArguments();
        foreach( var gen_type_arg in gen_type_args )
        {
            arg_names.Add(
                recursion_level != 0
                    ? pretty_name( gen_type_arg, recursion_level - 1, expand_nullable )
                    : "?" );
        }

        // if type is nullable and want compact notation '?'
        if( !expand_nullable && Nullable.GetUnderlyingType( type ) != null )
            return $"{arg_names[ 0 ]}?";

        // compose common generic type format "T<T1, T2, ...>"
        return $"{gen_type_name}<{string.Join( ", ", arg_names )}>";
    }

    return type.Name;
}

我的两分钱:

  • 一切都是通过 Type 接口完成的
  • 没有正则表达式
  • 除了保存通用参数名称的列表之外,没有创建额外的对象
  • 支持无限递归或递归到一定级别(或根本不递归!)
  • 支持可为空类型(两种格式“ Nullable<>”和“?”)
  • 支持排序数组
public static string pretty_name( Type type, int recursion_level = -1, bool expand_nullable = false )
{
    if( type.IsArray )
    {
        return 
quot;{pretty_name( type.GetElementType(), recursion_level, expand_nullable )}[]";
    }

    if( type.IsGenericType )
    {
        // find generic type name
        var gen_type_name = type.GetGenericTypeDefinition().Name;
        var index = gen_type_name.IndexOf( '`' );
        if( index != -1 )
            gen_type_name = gen_type_name.Substring( 0, index );

        // retrieve generic type aguments
        var arg_names = new List<string>();
        var gen_type_args = type.GetGenericArguments();
        foreach( var gen_type_arg in gen_type_args )
        {
            arg_names.Add(
                recursion_level != 0
                    ? pretty_name( gen_type_arg, recursion_level - 1, expand_nullable )
                    : "?" );
        }

        // if type is nullable and want compact notation '?'
        if( !expand_nullable && Nullable.GetUnderlyingType( type ) != null )
            return 
quot;{arg_names[ 0 ]}?";

        // compose common generic type format "T<T1, T2, ...>"
        return 
quot;{gen_type_name}<{string.Join( ", ", arg_names )}>";
    }

    return type.Name;
}

My two cents:

  • Everything is done through the Type interface
  • No Regex
  • No extra objects created except for the list that holds generic argument names
  • Supports infinite recursion or recursion to a certain level (or no recursion at all!)
  • Supports nullable types (both formats "Nullable<>" and "?")
  • Supports ranked arrays
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文