在Roslyn中使用泛型生成有效类型名的技术
本文关键字:类型 有效 技术 Roslyn 泛型 | 更新日期: 2023-09-27 18:00:09
我正在尝试一些可能的技术,以便在运行时生成C#接口的动态代理。到目前为止,我发现Roslyn在没有太多摩擦的情况下给了我很大的帮助,但我有点拘泥于处理泛型类型。特别是,获取要解析的类型名称。
我的基本工作流程是:
- 为使用、命名空间和类作为
CompilationUnitSyntax
构建脚手架 - 检查接口是否被代理
- 对于接口上的每个方法,使用
MethodInfo
使用SyntaxFactory.MethodDeclaration
构建MethodDeclarationSyntax
,目标是我的新动态类
这是我困惑的问题的一个例子。在这一点上,我似乎需要解析一个字符串来获得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}
它显然不会解析,有反勾号,缺少尖括号等。
反射样式类(MethodInfo,Types)和Roslyn语法单元之间有更好的桥梁吗?我也在尝试一个纯反射发射风格的解决方案,但我想看看是否可以在这里找到一个基于Roslyn的解决方案。
我创建了这个扩展方法来解决这个问题。
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 );
}
}
为了从泛型类型创建TypeSyntax,这个静态工厂类可能会有所帮助。您需要获得想要生成的类型的泛型参数列表,但我在底部发布的工厂(在本文要点中也可用)帮助我相对轻松地获得了TypeSyntax
的实例。
如何使用示例:
// List<Dictionary<string, List<Type>>>
TypeSyntaxFactory.GetTypeSyntax(
"List",
TypeSyntaxFactory.GetTypeSyntax(
"Dictionary",
TypeSyntaxFactory.GetTypeSyntax(
"string"
),
TypeSyntaxFactory.GetTypeSyntax(
"List",
"Type"
)
)
)
我不确定处理反射输出的最佳方法是什么,但您可能只需要在"`"符号之前取类型标识符的子字符串。在我的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;
}
}
)
)
)
);
}
}