在Roslyn中使用泛型生成有效类型名称的技术

edd*_*oll 7 c# reflection code-generation roslyn

我正在尝试一些可能的技术,用于在运行时生成C#接口的动态代理.到目前为止,我发现Roslyn在没有太多摩擦的情况下带我走了一段路,但我对处理泛型类型有点困惑.特别是,要解析类型名称.

我的基本工作流程是:

  • 为使用,命名空间和类构建脚手架 CompilationUnitSyntax
  • 检查接口是否已获得代理
  • 对于界面上的每个方法,使用MethodInfo构建一个MethodDeclarationSyntaxusing SyntaxFactory.MethodDeclaration,目标是我的新动态类

这是我困惑的问题的一个例子.在这一点上,似乎我需要解析一个字符串来获得一个TypeSyntax(在这种情况下为返回类型),并且我唯一可以接受它的地方是methodInfo.ReturnType.Name:

var methodDecl = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(methodInfo.ReturnType.Name), methodInfo.Name);

例如,问题是SyntaxFactory.ParseTypeName期望'有效'的C#语法类型声明List<string>,但访问Name或FullName属性的形式如下:

{Name = "List`1" FullName =
"System.Collections.Generic.List`1[[UnitTests.SamplePoco, UnitTests,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}   System.Type
{System.RuntimeType}
Run Code Online (Sandbox Code Playgroud)

这显然不会用反引号解析,缺少尖括号等.

Reflection样式类(MethodInfo,Types)和Roslyn语法单元之间是否有更好的桥梁?我也尝试了一种纯粹的反射发射方式解决方案,但是想知道我是否可以在这里找到一个基于Roslyn的方法.

Hap*_*mad 7

我创建了这个扩展方法来解决这个问题。

static class SyntaxExtensions
{
    /// <summary>
    /// Generates the type syntax.
    /// </summary>
    public static TypeSyntax AsTypeSyntax( this Type type )
    {
        string name = type.Name.Replace( '+', '.' );

        if ( type.IsGenericType ) {
            // Get the C# representation of the generic type minus its type arguments.
            name = name.Substring( 0, name.IndexOf( "`" ) );

            // Generate the name of the generic type.
            var genericArgs = type.GetGenericArguments();
            return SyntaxFactory.GenericName( SyntaxFactory.Identifier( name ),
                SyntaxFactory.TypeArgumentList( SyntaxFactory.SeparatedList( genericArgs.Select( AsTypeSyntax ) ) )
            );
        } else
            return SyntaxFactory.ParseTypeName( name );
    }
}
Run Code Online (Sandbox Code Playgroud)


Fra*_*yce 3

为了从泛型类型创建 TypeSyntax,这个静态工厂类可能会有所帮助。您将需要获取您希望生成的类型的通用参数列表,但我在底部发布的工厂(也可在本要点中找到)帮助我相对轻松地获得了实例TypeSyntax

如何使用它的示例:

// List<Dictionary<string, List<Type>>>
TypeSyntaxFactory.GetTypeSyntax(
    "List",
    TypeSyntaxFactory.GetTypeSyntax(
        "Dictionary",
        TypeSyntaxFactory.GetTypeSyntax(
            "string"
        ),
        TypeSyntaxFactory.GetTypeSyntax(
            "List",
            "Type"
        )
    )
)
Run Code Online (Sandbox Code Playgroud)

我不确定处理反射输出的最佳方法是什么,但您可能只需在“`”符号之前获取类型标识符的子字符串。在我的 IDE 中,此符号不是类型名称的有效字符,因此可以安全地假设它是反射类型输出的一部分。

最后,这是这个要点的副本

public static class TypeSyntaxFactory
{
    /// <summary>
    /// Used to generate a type without generic arguments
    /// </summary>
    /// <param name="identifier">The name of the type to be generated</param>
    /// <returns>An instance of TypeSyntax from the Roslyn Model</returns>
    public static TypeSyntax GetTypeSyntax(string identifier)
    {
        return
            SyntaxFactory.IdentifierName(
                SyntaxFactory.Identifier(identifier)
            );
    }

    /// <summary>
    /// Used to generate a type with generic arguments
    /// </summary>
    /// <param name="identifier">Name of the Generic Type</param>
    /// <param name="arguments">
    /// Types of the Generic Arguments, which must be basic identifiers
    /// </param>
    /// <returns>An instance of TypeSyntax from the Roslyn Model</returns>
    public static TypeSyntax GetTypeSyntax(string identifier, params string[] arguments)
    {
        return GetTypeSyntax(identifier, arguments.Select(GetTypeSyntax).ToArray());
    }

    /// <summary>
    /// Used to generate a type with generic arguments
    /// </summary>
    /// <param name="identifier">Name of the Generic Type</param>
    /// <param name="arguments">
    /// Types of the Generic Arguments, which themselves may be generic types
    /// </param>
    /// <returns>An instance of TypeSyntax from the Roslyn Model</returns>
    public static TypeSyntax GetTypeSyntax(string identifier, params TypeSyntax[] arguments)
    {
        return
            SyntaxFactory.GenericName(
                SyntaxFactory.Identifier(identifier),
                SyntaxFactory.TypeArgumentList(
                    SyntaxFactory.SeparatedList(
                        arguments.Select(
                            x =>
                            {
                                if(x is GenericNameSyntax)
                                {
                                    var gen_x = x as GenericNameSyntax;
                                    return
                                        GetTypeSyntax(
                                            gen_x.Identifier.ToString(),
                                            gen_x.TypeArgumentList.Arguments.ToArray()
                                        );
                                }
                                else
                                {
                                    return x;
                                }
                            }
                        )
                    )
                )
            );
    }
}
Run Code Online (Sandbox Code Playgroud)