如何解析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