【问题标题】:Get the type name获取类型名称
【发布时间】:2011-02-04 12:04:11
【问题描述】:

如何获得泛型类型的完整名称?

例如: 这段代码

typeof(List<string>).Name

返回

列表`1

而不是

List<string>

如何取一个合适的名字?

typeof(List<string>).ToString()

返回 System.Collections.Generic.List`1[System.String] 但我想获得初始名称:

List<string>

是真的吗?

【问题讨论】:

  • 这是否特定于 .NET 的某些版本?

标签: c# generics


【解决方案1】:

使用FullName property

typeof(List<string>).FullName

这将为您提供命名空间 + 类 + 类型参数。

您要求的是特定于 C# 的语法。就 .NET 而言,这是正确的:

System.Collections.Generic.List`1[System.String]

所以要得到你想要的,你必须编写一个函数来按照你想要的方式构建它。或许是这样:

static string GetCSharpRepresentation( Type t, bool trimArgCount ) {
    if( t.IsGenericType ) {
        var genericArgs = t.GetGenericArguments().ToList();

        return GetCSharpRepresentation( t, trimArgCount, genericArgs );
    }

    return t.Name;
}

static string GetCSharpRepresentation( Type t, bool trimArgCount, List<Type> availableArguments ) {
    if( t.IsGenericType ) {
        string value = t.Name;
        if( trimArgCount && value.IndexOf("`") > -1 ) {
            value = value.Substring( 0, value.IndexOf( "`" ) );
        }

        if( t.DeclaringType != null ) {
            // This is a nested type, build the nesting type first
            value = GetCSharpRepresentation( t.DeclaringType, trimArgCount, availableArguments ) + "+" + value;
        }

        // Build the type arguments (if any)
        string argString = "";
        var thisTypeArgs = t.GetGenericArguments();
        for( int i = 0; i < thisTypeArgs.Length && availableArguments.Count > 0; i++ ) {
            if( i != 0 ) argString += ", ";

            argString += GetCSharpRepresentation( availableArguments[0], trimArgCount );
            availableArguments.RemoveAt( 0 );
        }

        // If there are type arguments, add them with < >
        if( argString.Length > 0 ) {
            value += "<" + argString + ">";
        }

        return value;
    }

    return t.Name;
}

对于这些类型(第二个参数为 true):

typeof( List<string> ) )
typeof( List<Dictionary<int, string>> )

返回:

List<String>
List<Dictionary<Int32, String>>

但总的来说,我敢打赌您可能不需要需要 拥有代码的 C# 表示形式,如果您这样做了,那么某种比 C# 语法更好的格式可能会更合适。

【讨论】:

  • 输出:System.Collections.Generic.List`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
  • 已经更新了 nobugz(他的说明原本没有):)
  • 亚当,你的代码不正确。尝试使用 class C { public class D { } } 和 GetCSharpRepresentation(typeof(C.D))。你没有得到 C# 表示。
  • 那么只有嵌套类型和嵌套类型,其中基类型具有类型参数?鉴于对 为什么 他无论如何都会做这件愚蠢的事情的理解,我不怀疑这段代码首先没有什么合法用途,更不用说完整功能的“获取类型的 c# 表示”了名称”函数。
  • 呸。嘿,为了完整起见,我继续更新示例以处理嵌套类型(通用和非通用)。它似乎适用于我的所有场景。
【解决方案2】:

你可以用这个:

public static string GetTypeName(Type t) {
  if (!t.IsGenericType) return t.Name;
  if (t.IsNested && t.DeclaringType.IsGenericType) throw new NotImplementedException();
  string txt = t.Name.Substring(0, t.Name.IndexOf('`')) + "<";
  int cnt = 0;
  foreach (Type arg in t.GetGenericArguments()) {
    if (cnt > 0) txt += ", ";
    txt += GetTypeName(arg);
    cnt++;
  }
  return txt + ">";
}

例如:

static void Main(string[] args) {
  var obj = new Dictionary<string, Dictionary<HashSet<int>, int>>();
  string s = GetTypeName(obj.GetType());
  Console.WriteLine(s);
  Console.ReadLine();
}

输出:

Dictionary<String, Dictionary<HashSet<Int32>, Int32>>

【讨论】:

  • 这是不正确的,原因与 Adam 的代码不正确的原因相同;您还没有考虑 CLR 如何表示嵌套的泛型类型。
【解决方案3】:

如果你有列表的实例,你可以调用 .ToString() 并得到以下内容

System.Collections.Generic.List`1[System.String]

这是对其他答案直接针对类型而不是实例提供的方法的补充。

编辑:在您的编辑中,我认为不提供您自己的解析方法是不可能的,因为List&lt;string&gt; 是 C# 类型实现方式的简写,有点像您写的typeof(int).ToString(),捕获的不是“int”而是CTS名称,System.Int32

【讨论】:

    【解决方案4】:

    这是我的实现,受益于上面@Hans 的回答和@Jack 在duplicate question 上的回答。

    public static string GetCSharpName( this Type type )
    {
        string result;
        if ( primitiveTypes.TryGetValue( type, out result ) )
            return result;
        else
            result = type.Name.Replace( '+', '.' );
    
        if ( !type.IsGenericType )
            return result;
        else if ( type.IsNested && type.DeclaringType.IsGenericType )
            throw new NotImplementedException();
    
        result = result.Substring( 0, result.IndexOf( "`" ) );
        return result + "<" + string.Join( ", ", type.GetGenericArguments().Select( GetCSharpName ) ) + ">";
    }
    
    static Dictionary<Type, string> primitiveTypes = new Dictionary<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" },
    };
    

    【讨论】:

      【解决方案5】:

      另一种通过使用扩展来获得好听的类型名称的方法:

      typeof(Dictionary<string, Dictionary<decimal, List<int>>>).CSharpName();
      // output is: 
      // Dictionary<String, Dictionary<Decimal, List<Int32>>>
      

      扩展代码:

      public static class TypeExtensions
      {
         public static string CSharpName(this Type type)
         {
             string typeName = type.Name;
      
             if (type.IsGenericType)
             {
                 var genArgs = type.GetGenericArguments();
      
                 if (genArgs.Count() > 0)
                 {
                     typeName = typeName.Substring(0, typeName.Length - 2);
      
                     string args = "";
      
                     foreach (var argType in genArgs)
                     {
                         string argName = argType.Name;
      
                         if (argType.IsGenericType)
                             argName = argType.CSharpName();
      
                         args += argName + ", ";
                     }
      
                     typeName = string.Format("{0}<{1}>", typeName, args.Substring(0, args.Length - 2));
                 }
             }
      
             return typeName;
         }        
      }
      

      【讨论】:

        【解决方案6】:
        typeof(List<string>).ToString()
        

        【讨论】:

        • 输出:System.Collections.Generic.List`1[System.String]
        【解决方案7】:

        Adam Sills's answer 的改进,适用于非泛型嵌套类型和泛型类型定义:

        public class TypeNameStringExtensions
        {
            public static string GetCSharpRepresentation(Type t)
            {
                return GetCSharpRepresentation(t, new Queue<Type>(t.GetGenericArguments()));
            }
            static string GetCSharpRepresentation(Type t, Queue<Type> availableArguments)
            {
                string value = t.Name;
                if (t.IsGenericParameter)
                {
                    return value;
                }
                if (t.DeclaringType != null)
                {
                    // This is a nested type, build the parent type first
                    value = GetCSharpRepresentation(t.DeclaringType, availableArguments) + "+" + value;
                }
                if (t.IsGenericType)
                {
                    value = value.Split('`')[0];
        
                    // Build the type arguments (if any)
                    string argString = "";
                    var thisTypeArgs = t.GetGenericArguments();
                    for (int i = 0; i < thisTypeArgs.Length && availableArguments.Count > 0; i++)
                    {
                        if (i != 0) argString += ", ";
        
                        argString += GetCSharpRepresentation(availableArguments.Dequeue());
                    }
        
                    // If there are type arguments, add them with < >
                    if (argString.Length > 0)
                    {
                        value += "<" + argString + ">";
                    }
                }
        
                return value;
            }
        
            [TestCase(typeof(List<string>), "List<String>")]
            [TestCase(typeof(List<Dictionary<int, string>>), "List<Dictionary<Int32, String>>")]
            [TestCase(typeof(Stupid<int>.Stupider<int>), "Stupid<Int32>+Stupider<Int32>")]
            [TestCase(typeof(Dictionary<int, string>.KeyCollection), "Dictionary<Int32, String>+KeyCollection")]
            [TestCase(typeof(Nullable<Point>), "Nullable<Point>")]
            [TestCase(typeof(Point?), "Nullable<Point>")]
            [TestCase(typeof(TypeNameStringExtensions), "TypeNameStringExtensions")]
            [TestCase(typeof(Another), "TypeNameStringExtensions+Another")]
            [TestCase(typeof(G<>), "TypeNameStringExtensions+G<T>")]
            [TestCase(typeof(G<string>), "TypeNameStringExtensions+G<String>")]
            [TestCase(typeof(G<Another>), "TypeNameStringExtensions+G<TypeNameStringExtensions+Another>")]
            [TestCase(typeof(H<,>), "TypeNameStringExtensions+H<T1, T2>")]
            [TestCase(typeof(H<string, Another>), "TypeNameStringExtensions+H<String, TypeNameStringExtensions+Another>")]
            [TestCase(typeof(Another.I<>), "TypeNameStringExtensions+Another+I<T3>")]
            [TestCase(typeof(Another.I<int>), "TypeNameStringExtensions+Another+I<Int32>")]
            [TestCase(typeof(G<>.Nested), "TypeNameStringExtensions+G<T>+Nested")]
            [TestCase(typeof(G<string>.Nested), "TypeNameStringExtensions+G<String>+Nested")]
            [TestCase(typeof(A<>.C<>), "TypeNameStringExtensions+A<B>+C<D>")]
            [TestCase(typeof(A<int>.C<string>), "TypeNameStringExtensions+A<Int32>+C<String>")]
            public void GetCSharpRepresentation_matches(Type type, string expected)
            {
                string actual = GetCSharpRepresentation(type);
                Assert.AreEqual(expected, actual);
            }
        
            public class G<T>
            {
                public class Nested { }
            }
        
            public class A<B>
            {
                public class C<D> { }
            }
        
            public class H<T1, T2> { }
        
            public class Another
            {
                public class I<T3> { }
            }
        }
        
        public class Stupid<T1>
        {
            public class Stupider<T2>
            {
            }
        }
        

        我还选择了放弃他的trimArgCount,因为我看不出什么时候会有用,而是使用Queue&lt;Type&gt;,因为这是我的意图(当它们存在时将它们从前面拉出来)。

        【讨论】:

          【解决方案8】:

          在某些情况下,我遇到了其他答案的问题,即数组,所以我最终又写了一个。我不使用来自Type.Name 或类似的文本,除了获取类型的纯名称,因为我不知道格式是否保证在不同的.Net 版本或与库的其他实现中相同(我认为不是)。

          /// <summary>
          /// For the given type, returns its representation in C# code.
          /// </summary>
          /// <param name="type">The type.</param>
          /// <param name="genericArgs">Used internally, ignore.</param>
          /// <param name="arrayBrackets">Used internally, ignore.</param>
          /// <returns>The representation of the type in C# code.</returns>
          
          public static string GetTypeCSharpRepresentation(Type type, Stack<Type> genericArgs = null, StringBuilder arrayBrackets = null)
          {
              StringBuilder code = new StringBuilder();
              Type declaringType = type.DeclaringType;
          
              bool arrayBracketsWasNull = arrayBrackets == null;
          
              if (genericArgs == null)
                  genericArgs = new Stack<Type>(type.GetGenericArguments());
          
          
              int currentTypeGenericArgsCount = genericArgs.Count;
              if (declaringType != null)
                  currentTypeGenericArgsCount -= declaringType.GetGenericArguments().Length;
          
              Type[] currentTypeGenericArgs = new Type[currentTypeGenericArgsCount];
              for (int i = currentTypeGenericArgsCount - 1; i >= 0; i--)
                  currentTypeGenericArgs[i] = genericArgs.Pop();
          
          
              if (declaringType != null)
                  code.Append(GetTypeCSharpRepresentation(declaringType, genericArgs)).Append('.');
          
          
              if (type.IsArray)
              {
                  if (arrayBrackets == null)
                      arrayBrackets = new StringBuilder();
          
                  arrayBrackets.Append('[');
                  arrayBrackets.Append(',', type.GetArrayRank() - 1);
                  arrayBrackets.Append(']');
          
                  Type elementType = type.GetElementType();
                  code.Insert(0, GetTypeCSharpRepresentation(elementType, arrayBrackets : arrayBrackets));
              }
              else
              {
                  code.Append(new string(type.Name.TakeWhile(c => char.IsLetterOrDigit(c) || c == '_').ToArray()));
          
                  if (currentTypeGenericArgsCount > 0)
                  {
                      code.Append('<');
                      for (int i = 0;  i < currentTypeGenericArgsCount;  i++)
                      {
                          code.Append(GetTypeCSharpRepresentation(currentTypeGenericArgs[i]));
                          if (i < currentTypeGenericArgsCount - 1)
                              code.Append(',');
                      }
                      code.Append('>');
                  }
          
                  if (declaringType == null  &&  !string.IsNullOrEmpty(type.Namespace))
                  {
                      code.Insert(0, '.').Insert(0, type.Namespace);
                  }
              }
          
          
              if (arrayBracketsWasNull  &&  arrayBrackets != null)
                  code.Append(arrayBrackets.ToString());
          
          
              return code.ToString();
          }
          

          我已经用这样的疯狂类型对其进行了测试,到目前为止它运行良好:

          class C
          {
              public class D<D1, D2>
              {
                  public class E
                  {
                      public class K<R1, R2, R3>
                      {
                          public class P<P1>
                          {
                              public struct Q
                              {
                              }
                          }
                      }
                  }
              }
          }
          
          type = typeof(List<Dictionary<string[], C.D<byte, short[,]>.E.K<List<int>[,][], Action<List<long[][][,]>[], double[][,]>, float>.P<string>.Q>>[][,][,,,][][,,]);
          
          // Returns "System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String[],Test.Program.C.D<System.Byte,System.Int16[,]>.E.K<System.Collections.Generic.List<System.Int32>[,][],System.Action<System.Collections.Generic.List<System.Int64[][][,]>[],System.Double[][,]>,System.Single>.P<System.String>.Q>>[][,][,,,][][,,]":
          GetTypeCSharpRepresentation(type);
          

          可能还有一些我没有想到的问题,但有一个已知问题:为了检索名称,我只获取满足条件char.IsLetterOrDigit(c) || c == '_' 的字符,直到没有找到的字符,所以任何名称使用不符合条件的允许字符的类型将失败。

          【讨论】:

            【解决方案9】:

            遇到这个,我想我会分享我自己的解决方案。它处理多个泛型参数、可为空值、锯齿状数组、多维数组、锯齿状/多维数组的组合,以及上述任何一种的任何嵌套组合。我主要用它来记录日志,以便更容易识别复杂的类型。

            public static string GetGoodName(this Type type)
            {
                var sb = new StringBuilder();
            
                void VisitType(Type inType)
                {
                    if (inType.IsArray)
                    {
                        var rankDeclarations = new Queue<string>();
                        Type elType = inType;
            
                        do
                        {
                            rankDeclarations.Enqueue($"[{new string(',', elType.GetArrayRank() - 1)}]");
                            elType = elType.GetElementType();
                        } while (elType.IsArray);
            
                        VisitType(elType);
            
                        while (rankDeclarations.Count > 0)
                        {
                            sb.Append(rankDeclarations.Dequeue());
                        }
                    }
                    else
                    {
                        if (inType.IsGenericType)
                        {
                            var isNullable = inType.IsNullable();
                            var genargs = inType.GetGenericArguments().AsEnumerable();
                            var numer = genargs.GetEnumerator();
            
                            numer.MoveNext();
            
                            if (!isNullable) sb.Append($"{inType.Name.Substring(0, inType.Name.IndexOf('`'))}<");
            
                            VisitType(numer.Current);
            
                            while (numer.MoveNext())
                            {
                                sb.Append(",");
                                VisitType(numer.Current);
                            }
            
                            if (isNullable)
                            {
                                sb.Append("?");
                            }
                            else
                            {
                                sb.Append(">");
                            }
                        }
                        else
                        {
                            sb.Append(inType.Name);
                        }
                    }
                }
            
                VisitType(type);
            
                var s = sb.ToString();
            
                return s;
            }
            

            这个:

            typeof(Dictionary<int?, Tuple<string[], List<string[][,,,]>>>).GetGoodName()
            

            ...返回这个:

            Dictionary<Int32?,Tuple<String[],List<String[][,,,]>>>
            

            【讨论】:

              【解决方案10】:

              如果您想使用基本泛型类型:

              List<string> lstString = new List<string>();
              Type type = lstString.GetType().GetGenericTypeDefinition();
              

              假设您想使用该类型做某事并且您并不真正需要实际的字符串定义,这并不是那么有用。

              【讨论】:

                猜你喜欢
                • 2013-11-29
                • 1970-01-01
                • 1970-01-01
                • 2010-09-24
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多