如何用Roslyn从反射接口生成带可选参数的MethodDeclarationSyntax
问题
给定包含可选参数方法的接口定义:
interface SomeInterface { void SomeMethod(int a = 1, string c = "foo", SomeType e = default, SomeType f = null); } SomeInterfaceType = typeof(SomeInterface);
需要通过反射类型创建MethodDeclarationSyntax,目前已实现从MethodInfo生成参数列表的方法,但不知道如何为可选参数生成对应的EqualsValueClause来完善WithDefault方法:
private ParameterListSyntax GenerateParameterListSyntax(MethodInfo info) { var parameters = info.GetParameters(); var parameterList = new SeparatedSyntaxList<ParameterSyntax>(); foreach (var parameter in parameters) { var parameterSyntax = SyntaxFactory.Parameter(SyntaxFactory.Identifier(parameter.Name)) .WithType(SyntaxFactory.ParseTypeName(TypeNameOrAlias(parameter.ParameterType))); if (parameter.IsOptional) { parameterSyntax = parameterSyntax.WithDefault( <<< 此处需要实现 >>> ); } parameterList.Add(parameterSyntax); } return SyntaxFactory.ParameterList(parameterList); }
附RoslynQuoter生成的参考语法树代码(对应原接口的语法结构):
CompilationUnit() .WithMembers( SingletonList<MemberDeclarationSyntax>( InterfaceDeclaration("SomeInterface") .WithMembers( SingletonList<MemberDeclarationSyntax>( MethodDeclaration( PredefinedType( Token(SyntaxKind.VoidKeyword)), Identifier("SomeMethod")) .WithParameterList( ParameterList( SeparatedList<ParameterSyntax>( new SyntaxNodeOrToken[]{ Parameter( Identifier("a")) .WithType( PredefinedType( Token(SyntaxKind.IntKeyword))) .WithDefault( EqualsValueClause( LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(1)))), Token(SyntaxKind.CommaToken), Parameter( Identifier("c")) .WithType( PredefinedType( Token(SyntaxKind.StringKeyword))) .WithDefault( EqualsValueClause( LiteralExpression( SyntaxKind.StringLiteralExpression, Literal("foo")))), Token(SyntaxKind.CommaToken), Parameter( Identifier("e")) .WithType( IdentifierName("SomeType")) .WithDefault( EqualsValueClause( LiteralExpression( SyntaxKind.DefaultLiteralExpression, Token(SyntaxKind.DefaultKeyword)))), Token(SyntaxKind.CommaToken), Parameter( Identifier("f")) .WithType( IdentifierName("SomeType")) .WithDefault( EqualsValueClause( LiteralExpression( SyntaxKind.NullLiteralExpression)))}))) .WithSemicolonToken( Token(SyntaxKind.SemicolonToken)))))) .NormalizeWhitespace()
解决方案
要生成对应可选参数的EqualsValueClause,需要根据参数默认值的类型和具体值创建不同的语法节点,以下是完整实现:
核心逻辑
- 通过
parameter.DefaultValue获取可选参数的默认值 - 根据值的类型生成匹配的表达式语法节点:
null值:生成NullLiteralExpression- 值类型的
default:反射中以DBNull返回,对应生成DefaultLiteralExpression - 数值类型:生成
NumericLiteralExpression - 字符串类型:生成
StringLiteralExpression - 其他复杂类型:可按需扩展逻辑,默认用
default关键字表达式
修改后的完整方法
private ParameterListSyntax GenerateParameterListSyntax(MethodInfo info) { var parameters = info.GetParameters(); var parameterList = new SeparatedSyntaxList<ParameterSyntax>(); foreach (var parameter in parameters) { var parameterSyntax = SyntaxFactory.Parameter(SyntaxFactory.Identifier(parameter.Name)) .WithType(SyntaxFactory.ParseTypeName(TypeNameOrAlias(parameter.ParameterType))); if (parameter.IsOptional) { ExpressionSyntax defaultValueExpr; var defaultValue = parameter.DefaultValue; if (defaultValue == null) { // 处理null默认值 defaultValueExpr = SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression); } else if (defaultValue is DBNull) { // 值类型的default关键字场景 defaultValueExpr = SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression); } else if (defaultValue is int || defaultValue is long || defaultValue is float || defaultValue is double) { // 数值类型字面量 defaultValueExpr = SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(defaultValue.ToString())); } else if (defaultValue is string strValue) { // 字符串类型字面量 defaultValueExpr = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(strValue)); } else { // 其他未覆盖的类型,默认生成default关键字 defaultValueExpr = SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression); } // 创建EqualsValueClause并绑定到参数 var equalsClause = SyntaxFactory.EqualsValueClause(defaultValueExpr); parameterSyntax = parameterSyntax.WithDefault(equalsClause); } parameterList = parameterList.Add(parameterSyntax); } return SyntaxFactory.ParameterList(parameterList); }
补充说明
- 如果需要支持枚举类型的默认值,可以添加判断逻辑,通过
Enum.GetName获取枚举名称,生成IdentifierNameSyntax作为表达式 - 自定义结构体的默认值场景,反射返回的也是
DBNull,直接用DefaultLiteralExpression即可匹配原代码的default(SomeType)语法
内容的提问来源于stack exchange,提问作者xvan
相关产品推荐
相关产品推荐

