【问题标题】:C#: "Pretty" type name function?C#:“漂亮”类型名称函数?
【发布时间】:2011-06-19 14:12:08
【问题描述】:

System.Type 类的名称属性在泛型类型的情况下返回一个奇怪的结果。有没有办法以更接近我指定方式的格式获取类型名称? 示例:typeof(List<string>).OriginalName == "List<string>"

【问题讨论】:

  • 这很容易使用递归将自己编写为扩展方法。
  • 框架中没有这样的函数,因为它取决于你使用的语言:VB有不同的泛型表示,我相信其他语言也有。

标签: c# generics types pretty-print


【解决方案1】:

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

但是,自己制作这个相当容易:

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

这将递归解析非托管名称,因此如果您有类似 Dictionary&lt;string, IList&lt;string&gt;&gt; 的名称,它应该仍然可以工作。

【讨论】:

  • 理想情况下,这需要进一步开发来处理像Dictionary&lt;string,IList&lt;string&gt;&gt;.KeyCollection 这样的类型。它的 CLR 类型名称是 Dictionary`2+KeyCollection[System.String,IList`1[System.String]](给予或接受命名空间限定符)。此外,您可以拥有Outer`1+Inner`1[OuterArg,InnerArg] 之类的东西。当然,您可以拥有需要处理的数组(1D 2D 等)和指向泛型类型的指针。 :)
【解决方案2】:

我用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);
    }

【讨论】:

  • 如果我想省略命名空间怎么办?
  • 您可以使用 type.Name 而不是 type.FullName 我猜这样您就不必自己删除命名空间。抱歉,我现在看到您没有以这种方式获取泛型参数。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    这是我的实现。它是为了描述方法而创建的,因此它处理 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() 添加会相当容易。

    【讨论】:

      【解决方案5】:

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

      请进一步注意string 以及诸如System.String 等CLR 类型的别名。同样,这在您看到的格式中起作用。

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

      【讨论】:

        【解决方案6】:

        利用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&lt;IEnumerable&lt;Tuple&lt;Nullable&lt;int&gt;, string&gt;&gt;&gt;[,]。唯一没有处理好的特殊情况是 Nullable 类型,但这实际上更多是 CodeDomProvider 的错误。

        【讨论】:

          【解决方案7】:

          首先:感谢 Navid 避免车轮改造。如果可以的话,我会投票。

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

          希望 Roslyn 让这一切变得非常容易。

          【讨论】:

          • 抱歉,Nullable 模式被打乱了。应该是 @"System\.Nullable[\w\.]+)&gt;";
          【解决方案8】:

          在您的示例中,您可以预期该类型,以便您可以尝试

          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
          }
          

          【讨论】:

            【解决方案9】:

            我了解到您想比较类型。
            最好的方法是...
            myVar is List&lt;string&gt;
            myVar.GetType() == myOtherVar.GetType()

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

            【讨论】:

              【解决方案10】:

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

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

              【讨论】:

                【解决方案11】:

                我是这样的..

                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 。

                【讨论】:

                  【解决方案12】:

                  结合了几个答案,增​​加了对嵌套类型和数组等级的支持,并把它变成了一个带有缓存解析名称的扩展方法。

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

                  【讨论】:

                    【解决方案13】:
                    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”和“?”两种格式)
                    • 支持排名数组

                    【讨论】:

                      猜你喜欢
                      • 2012-04-02
                      • 2021-06-22
                      • 1970-01-01
                      • 2012-04-17
                      • 1970-01-01
                      • 2021-10-14
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多