如何解析C#泛型类型名?
如何解析格式为如何解析C#泛型类型名?,c#,parsing,typename,C#,Parsing,Typename,如何解析格式为列表或字典或更复杂的字典的C#风格泛型类型名称。假设这些名称是字符串,可能并不实际表示现有类型。它应该能够轻松地解析BoguClass。明确地说,我对解析.NET内部类型名称(格式为List`1[[System.Int32]]])不感兴趣,而是解析实际的C类型名称,就像它们出现在源代码中一样,使用或不使用点表示法的命名空间限定符 正则表达式被删除,因为它们是嵌套结构。我想System.CodeDom.CodeTypeReference构造函数可能会为我解析它,因为它有string
列表
或字典
或更复杂的字典
的C#风格泛型类型名称。假设这些名称是字符串,可能并不实际表示现有类型。它应该能够轻松地解析BoguClass
。明确地说,我对解析.NET内部类型名称(格式为List`1[[System.Int32]]]
)不感兴趣,而是解析实际的C类型名称,就像它们出现在源代码中一样,使用或不使用点表示法的命名空间限定符
正则表达式被删除,因为它们是嵌套结构。我想System.CodeDom.CodeTypeReference构造函数可能会为我解析它,因为它有string BaseType
和CodeTypeReferenceCollection 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<ArrayDimension> ArrayDimensions;
public List<TypeName> 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<ArrayDimension>();
TypeArguments = new List<TypeName>();
}
public static string MatchStructure( TypeName toMatch, TypeName toType )
{
return null;
}
public override string ToString()
{
string str = Name;
if (IsGeneric)
str += "<" + string.Join( ",", TypeArguments.Select<TypeName,string>( 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<ArrayDimension,string>( d => d.ToString() ) ) + "\r\n";
if (IsGeneric)
{
str += spacing + "GenericParameters: {\r\n" + string.Join( spacing + "},{\r\n", TypeArguments.Select<TypeName,string>( 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;
}
}
嗯,我用
Regex
和命名的捕获组(?组)
编写了这个小解析类,非常有趣
我的方法是,每个“类型定义”字符串可以分解为一组以下内容:类型名称、可选的泛型类型、可选的数组标记“[]”
因此,给定经典的字典
,您将使用字典
作为类型名称和字符串,字节[]
作为内部泛型类型字符串
我们可以在逗号(“,”)字符上拆分内部泛型类型,并使用相同的Regex
递归地解析每个类型字符串。每个成功的解析都应该添加到父类型信息中,您可以构建树层次结构
在前面的示例中,我们将得到一个要解析的{string,byte[]}
数组。这两种类型都很容易解析并设置为字典
内部类型的一部分
在ToString()
上,只需递归输出每个类型的友好名称,包括内部类型。因此,Dictionary
将输出他的类型名,并遍历所有内部类型,输出它们的类型名等等
class TypeInformation
{
static readonly Regex TypeNameRegex = new Regex(@"^(?<TypeName>[a-zA-Z0-9_]+)(<(?<InnerTypeName>[a-zA-Z0-9_,\<\>\s\[\]]+)>)?(?<Array>(\[\]))?$", RegexOptions.Compiled);
readonly List<TypeInformation> innerTypes = new List<TypeInformation>();
public string TypeName
{
get;
private set;
}
public bool IsArray
{
get;
private set;
}
public bool IsGeneric
{
get { return innerTypes.Count > 0; }
}
public IEnumerable<TypeInformation> InnerTypes
{
get { return innerTypes; }
}
private void AddInnerType(TypeInformation type)
{
innerTypes.Add(type);
}
private static IEnumerable<string> SplitByComma(string value)
{
var strings = new List<string>();
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();
}
}
类类型信息
{
静态只读正则表达式TypeNameRegex=新正则表达式(@“^(?[a-zA-Z0-9\]+)()?(?(\[\]))?$”,RegexOptions.Compiled);
只读列表innerTypes=新列表();
公共字符串类型名
{
得到;
私人设置;
}
公共图书馆
{
得到;
私人设置;
}
公共布尔是通用的
{
获取{return innerTypes.Count>0;}
}
公共IEnumerable内部类型
{
获取{return innerTypes;}
}
私有void附加类型(类型信息类型)
{
innerTypes.Add(type);
}
私有静态IEnumerable SplitByComma(字符串值)
{
var strings=新列表();
var sb=新的StringBuilder();
风险值水平=0;
foreach(价值中的var c)
{
如果(c==','&&level==0)
{
添加(sb.ToString());
(某人清楚地);
}
其他的
{
sb.附加(c);
}
如果(c='')
级别--;
}
添加(sb.ToString());
返回字符串;
}
公共静态bool TryParse(字符串友好型typename,out-TypeInformation-TypeInformation)
{
typeInformation=null;
//尝试将类型与正则表达式匹配。
var match=TypeNameRegex.match(friendlyTypeName);
//如果失败,则格式不正确。
如果(!match.Success)
返回false;
//清除类型名称、内部类型名称和数组“[]”标记(如果存在)。
var typeName=match.Groups[“typeName”].Value;
var innerTypeFriendlyName=match.Groups[“InnerTypeName”].Value;
var isArray=!string.IsNullOrWhiteSpace(match.Groups[“Array”].Value);
//创建根类型信息。
类型信息类型=新类型信息
{
TypeName=TypeName,
IsArray=IsArray
};
//检查是否有内部类型名(对于泛型)。
如果(!string.IsNullOrWhiteSpace(innerTypeFriendlyName))
{
//按逗号字符拆分每个类型。
var innerTypeNames=SplitByComma(innerTypeFriendlyName);
//遍历所有内部类型名并尝试递归地解析它们。
foreach(innerTypeName中的字符串innerTypeName)
{
类型信息innerType=null;
var trimmedInnerTypeName=innerTypeName.Trim();
var success=TypeInformation.TryParse(trimmedInnerTypeName,out-innerType);
//如果内部类型失败,则父类型也会失败。
如果(!成功)
返回false;
//成功!将内部类型添加到父级。
类型。AddInnerType(innerType);
}
}
//返回已解析的类型信息。
类型信息=类型;
返回true;
}
公共重写字符串ToString()
{
//创建一个类型名为前缀的字符串生成器。
var sb=新的StringBuilder(this.TypeName);
//如果此类型是泛型(具有内部类型),则递归地追加每个类型。
if(this.IsGeneric)
{
某人加上(“”);
}
//如果此类型是数组,则附加数组“[]”标记。
如果(此为IsArray)
某人加上“[]”;
使某人返回字符串();
}
}
我做了一个控制台应用程序来测试它,它似乎工作
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:
}
class TypeInformation
{
static readonly Regex TypeNameRegex = new Regex(@"^(?<TypeName>[a-zA-Z0-9_]+)(<(?<InnerTypeName>[a-zA-Z0-9_,\<\>\s\[\]]+)>)?(?<Array>(\[\]))?$", RegexOptions.Compiled);
readonly List<TypeInformation> innerTypes = new List<TypeInformation>();
public string TypeName
{
get;
private set;
}
public bool IsArray
{
get;
private set;
}
public bool IsGeneric
{
get { return innerTypes.Count > 0; }
}
public IEnumerable<TypeInformation> InnerTypes
{
get { return innerTypes; }
}
private void AddInnerType(TypeInformation type)
{
innerTypes.Add(type);
}
private static IEnumerable<string> SplitByComma(string value)
{
var strings = new List<string>();
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<A,B,Vector<C>>";
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<A,B,Vector<C>>
Output = BogusClass<A, B, Vector<C>>
Graph:
BogusClass
A
B
Vector
C