gpt4 book ai didi

c# - 如何解析 C# 泛型类型名称?

转载 作者:太空狗 更新时间:2023-10-29 20:24:10 24 4
gpt4 key购买 nike

如何解析格式为 List<int> 的 C# 样式泛型类型名称或 Dictionary<string,int>甚至更复杂 Dictionary<string,Dictionary<System.String,int[]>> .假设这些名称是字符串,实际上可能不代表现有类型。它应该能够轻松解析 BogusClass<A,B,Vector<C>> .明确地说,我对解析 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
}
}

框架中有现成的类来实现这种类型名解析吗?如果不是,我将如何解析它?

最佳答案

好吧,我在使用 Regex 编写这个小解析类时玩得很开心并命名为捕获组 (?<Name>group) .

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

所以给出了经典Dictionary<string, byte[]>你会有 Dictionary作为类型名称string, byte[]作为您的内部通用类型字符串。

我们可以在逗号 (',') 字符上拆分内部泛型类型,并使用相同的 Regex 递归解析每个类型字符串.每次成功的解析都应该添加到父类型信息中,您可以构建树状层次结构。

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

关于 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();
}
}

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

代码如下:

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

可能存在一些挥之不去的问题,例如多维数组。它很可能会在类似 int[,] 的情况下失败或 string[][] .

关于c# - 如何解析 C# 泛型类型名称?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20532691/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com