如何解析C#generics类型名称?

如何解析格式为ListDictionary C#样式generics类型名称,甚至更复杂的Dictionary<string,Dictionary> 。 假设这些名称是字符串,实际上可能不代表现有类型。 它应该很容易解析BogusClass<A,B,Vector> 。 为了清楚List`1[[System.Int32]] ,我对解析格式为List`1[[System.Int32]] .NET内部类型名称不感兴趣,而是对源代码中出现的实际C#类型名称感兴趣,使用点或不使用名称空间限定符符号。

正则表达式是因为这些是嵌套结构。 我想也许System.CodeDom.CodeTypeReference构造函数会为我解析它,因为它有string BaseTypeCodeTypeReferenceCollection TypeArguments成员,但那些显然需要手动设置。

CodeTypeReference是我需要的一种结构:

 class TypeNameStructure { public string Name; public TypeNameStructure[] GenericTypeArguments; public bool IsGenericType{get;} public bool IsArray{get;} //would be nice to detect this as well public TypeNameStructure( string friendlyCSharpName ) { //Parse friendlyCSharpName into name and generic type arguments recursively } } 

框架中是否有任何现有的类来实现这种类型名称解析? 如果没有,我将如何解析这个?

回答自己的问题。 我写了下面的类来实现我需要的结果; 给它一个旋转。

 public class TypeName { public string Name; public bool IsGeneric; public List ArrayDimensions; public List TypeArguments; public class ArrayDimension { public int Dimensions; public ArrayDimension() { Dimensions = 1; } public override string ToString() { return "[" + new String(',', Dimensions - 1) + "]"; } } public TypeName() { Name = null; IsGeneric = false; ArrayDimensions = new List(); TypeArguments = new List(); } public static string MatchStructure( TypeName toMatch, TypeName toType ) { return null; } public override string ToString() { string str = Name; if (IsGeneric) str += "<" + string.Join( ",", TypeArguments.Select( tn => tn.ToString() ) ) + ">"; foreach (ArrayDimension d in ArrayDimensions) str += d.ToString(); return str; } public string FormatForDisplay( int indent = 0 ) { var spacing = new string(' ', indent ); string str = spacing + "Name: " + Name + "\r\n" + spacing + "IsGeneric: " + IsGeneric + "\r\n" + spacing + "ArraySpec: " + string.Join( "", ArrayDimensions.Select( d => d.ToString() ) ) + "\r\n"; if (IsGeneric) { str += spacing + "GenericParameters: {\r\n" + string.Join( spacing + "},{\r\n", TypeArguments.Select( t => t.FormatForDisplay( indent + 4 ) ) ) + spacing + "}\r\n"; } return str; } public static TypeName Parse( string name ) { int pos = 0; bool dummy; return ParseInternal( name, ref pos, out dummy ); } private static TypeName ParseInternal( string name, ref int pos, out bool listTerminated ) { StringBuilder sb = new StringBuilder(); TypeName tn = new TypeName(); listTerminated = true; while (pos < name.Length) { char c = name[pos++]; switch (c) { case ',': if (tn.Name == null) tn.Name = sb.ToString(); listTerminated = false; return tn; case '>': if (tn.Name == null) tn.Name = sb.ToString(); listTerminated = true; return tn; case '<': { tn.Name = sb.ToString(); tn.IsGeneric = true; sb.Length = 0; bool terminated = false; while (!terminated) tn.TypeArguments.Add( ParseInternal( name, ref pos, out terminated ) ); var t = name[pos-1]; if (t == '>') continue; else throw new Exception( "Missing closing > of generic type list." ); } case '[': ArrayDimension d = new ArrayDimension(); tn.ArrayDimensions.Add( d ); analyzeArrayDimension: //label for looping over multidimensional arrays if (pos < name.Length) { char nextChar = name[pos++]; switch (nextChar) { case ']': continue; //array specifier terminated case ',': //multidimensional array d.Dimensions++; goto analyzeArrayDimension; default: throw new Exception( @"Expecting ""]"" or "","" after ""["" for array specifier but encountered """ + nextChar + @"""." ); } } throw new Exception( "Expecting ] or , after [ for array type, but reached end of string." ); default: sb.Append(c); continue; } } if (tn.Name == null) tn.Name = sb.ToString(); return tn; } } 

如果我运行以下内容:

  Console.WriteLine( TypeName.Parse( "System.Collections.Generic.Dictionary,int[],bool>" ).ToString() ); 

它正确地生成以下输出,将TypeName表示为字符串:

 Name: System.Collections.Generic.Dictionary IsGeneric: True ArraySpec: GenericParameters: { Name: Vector IsGeneric: True ArraySpec: GenericParameters: { Name: T IsGeneric: False ArraySpec: } },{ Name: int IsGeneric: True ArraySpec: [] GenericParameters: { Name: long IsGeneric: False ArraySpec: [] } },{ Name: bool IsGeneric: False ArraySpec: } 

好吧,我使用Regex和命名捕获组(?group)编写这个小解析类有很多乐趣。

我的方法是每个’类型定义’字符串可以分解为以下一组: 类型名称可选的 通用类型可选的 数组标记 ‘[]’。

因此,给定经典的Dictionary您将使用Dictionary作为类型名称string, byte[]作为内部generics类型字符串。

我们可以在逗号(’,’)字符上拆分内部generics类型,并使用相同的正则Regex递归地解析每个类型字符串。 应将每个成功的解析添加到父类型信息中,并且可以构建树层次结构。

在前面的例子中,我们最终会得到一个{string, byte[]}数组来解析。 这两个都很容易解析并设置为Dictionary的内部类型的一部分。

ToString()它只是递归输出每个类型的友好名称,包括内部类型。 所以Dictionary会输出他的类型名称,并遍历所有内部类型,输出他们的类型名称等等。

 class TypeInformation { static readonly Regex TypeNameRegex = new Regex(@"^(?[a-zA-Z0-9_]+)(<(?[a-zA-Z0-9_,\<\>\s\[\]]+)>)?(?(\[\]))?$", RegexOptions.Compiled); readonly List innerTypes = new List(); public string TypeName { get; private set; } public bool IsArray { get; private set; } public bool IsGeneric { get { return innerTypes.Count > 0; } } public IEnumerable InnerTypes { get { return innerTypes; } } private void AddInnerType(TypeInformation type) { innerTypes.Add(type); } private static IEnumerable SplitByComma(string value) { var strings = new List(); var sb = new StringBuilder(); var level = 0; foreach (var c in value) { if (c == ',' && level == 0) { strings.Add(sb.ToString()); sb.Clear(); } else { sb.Append(c); } if (c == '<') level++; if(c == '>') level--; } strings.Add(sb.ToString()); return strings; } public static bool TryParse(string friendlyTypeName, out TypeInformation typeInformation) { typeInformation = null; // Try to match the type to our regular expression. var match = TypeNameRegex.Match(friendlyTypeName); // If that fails, the format is incorrect. if (!match.Success) return false; // Scrub the type name, inner type name, and array '[]' marker (if present). var typeName = match.Groups["TypeName"].Value; var innerTypeFriendlyName = match.Groups["InnerTypeName"].Value; var isArray = !string.IsNullOrWhiteSpace(match.Groups["Array"].Value); // Create the root type information. TypeInformation type = new TypeInformation { TypeName = typeName, IsArray = isArray }; // Check if we have an inner type name (in the case of generics). if (!string.IsNullOrWhiteSpace(innerTypeFriendlyName)) { // Split each type by the comma character. var innerTypeNames = SplitByComma(innerTypeFriendlyName); // Iterate through all inner type names and attempt to parse them recursively. foreach (string innerTypeName in innerTypeNames) { TypeInformation innerType = null; var trimmedInnerTypeName = innerTypeName.Trim(); var success = TypeInformation.TryParse(trimmedInnerTypeName, out innerType); // If the inner type fails, so does the parent. if (!success) return false; // Success! Add the inner type to the parent. type.AddInnerType(innerType); } } // Return the parsed type information. typeInformation = type; return true; } public override string ToString() { // Create a string builder with the type name prefilled. var sb = new StringBuilder(this.TypeName); // If this type is generic (has inner types), append each recursively. if (this.IsGeneric) { sb.Append("<"); // Get the number of inner types. int innerTypeCount = this.InnerTypes.Count(); // Append each inner type's friendly string recursively. for (int i = 0; i < innerTypeCount; i++) { sb.Append(innerTypes[i].ToString()); // Check if we need to add a comma to separate from the next inner type name. if (i + 1 < innerTypeCount) sb.Append(", "); } sb.Append(">"); } // If this type is an array, we append the array '[]' marker. if (this.IsArray) sb.Append("[]"); return sb.ToString(); } } 

我做了一个控制台应用程序来测试它,它似乎适用于我抛出的大多数情况。

这是代码:

 class MainClass { static readonly int RootIndentLevel = 2; static readonly string InputString = @"BogusClass>"; public static void Main(string[] args) { TypeInformation type = null; Console.WriteLine("Input = {0}", InputString); var success = TypeInformation.TryParse(InputString, out type); if (success) { Console.WriteLine("Output = {0}", type.ToString()); Console.WriteLine("Graph:"); OutputGraph(type, RootIndentLevel); } else Console.WriteLine("Parsing error!"); } static void OutputGraph(TypeInformation type, int indentLevel = 0) { Console.WriteLine("{0}{1}{2}", new string(' ', indentLevel), type.TypeName, type.IsArray ? "[]" : string.Empty); foreach (var innerType in type.InnerTypes) OutputGraph(innerType, indentLevel + 2); } } 

这是输出:

 Input = BogusClass> Output = BogusClass> Graph: BogusClass A B Vector C 

存在一些可能的延迟问题,例如多维数组。 它很可能会失败,比如int[,]string[][]