如何在调用model.GetDeclaredSymbol时获取SynthesizedIntrinsicOperatorSymbol而非SourceUserDefinedOperatorSymbol
如何将自定义运算符的SourceUserDefinedOperatorSymbol转换为SynthesizedIntrinsicOperatorSymbol?
我在处理Roslyn运算符解析时遇到了一个棘手的问题:当我在自定义的System.Int32结构体中声明>运算符,并用GetDeclaredSymbol获取该运算符的符号时,得到的是SourceUserDefinedOperatorSymbol;但在实际代码中使用这个运算符(比如bool a = 3 > 5;)时,通过GetSymbolInfo拿到的却是SynthesizedIntrinsicOperatorSymbol。我需要让声明处获取的符号和使用处保持一致,也就是把SourceUserDefinedOperatorSymbol转换为对应的SynthesizedIntrinsicOperatorSymbol。
我的测试代码如下:
bool a = 3 > 5; namespace System { public struct Int32 { public static extern bool operator >(int a, int b); } public struct Boolean { } }
我现在有一个方法框架,需要完成这个转换逻辑:
public static IMethodSymbol GetDeclaredSymbol (OperatorDeclarationSyntax ods) { IMethodSymbol opSym = model.GetDeclaredSymbol(ods) ?? throw new Exception("Symbol not found"); if (opSym is SourceUserDefinedOperatorSymbol) { // 需要在这里实现转换逻辑 } return opSym; }
解决方案思路
核心原因是你自定义的System.Int32运算符其实是在覆盖Roslyn内置的intrinsic运算符,所以我们可以通过查找编译环境中内置的Int32运算符符号来实现转换。具体步骤如下:
- 从当前编译上下文获取系统内置的
System.Int32类型符号 - 根据自定义运算符的类型(比如
>对应op_GreaterThan)找到对应的内置运算符方法 - 返回这个内置运算符符号,它就是
SynthesizedIntrinsicOperatorSymbol类型
完整实现代码
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; public static IMethodSymbol GetDeclaredSymbol(OperatorDeclarationSyntax ods, SemanticModel model) { IMethodSymbol opSym = model.GetDeclaredSymbol(ods) ?? throw new InvalidOperationException("Failed to retrieve declared operator symbol"); if (opSym is SourceUserDefinedOperatorSymbol userDefinedOp) { // 获取编译环境中的内置System.Int32类型 var intrinsicIntType = model.Compilation.GetTypeByMetadataName("System.Int32"); if (intrinsicIntType == null) throw new InvalidOperationException("System.Int32 type not found in current compilation"); // 根据运算符类型映射到标准元数据名称 string operatorMetadataName = userDefinedOp.MethodKind switch { MethodKind.GreaterThan => WellKnownMemberNames.GreaterThanOperator, MethodKind.LessThan => WellKnownMemberNames.LessThanOperator, MethodKind.Equality => WellKnownMemberNames.EqualityOperator, // 可以根据需要扩展其他运算符类型 _ => throw new NotSupportedException($"Operator kind {userDefinedOp.MethodKind} is not supported") }; // 查找对应的内置运算符符号 var intrinsicOperator = intrinsicIntType.GetMembers(operatorMetadataName) .OfType<IMethodSymbol>() .FirstOrDefault(method => method.Parameters.Length == 2 && method.Parameters[0].Type.Equals(intrinsicIntType) && method.Parameters[1].Type.Equals(intrinsicIntType)); // 找到则返回内置符号,否则返回原符号 return intrinsicOperator ?? opSym; } return opSym; }
代码说明
- 获取内置Int32类型:通过
Compilation.GetTypeByMetadataName获取的是Roslyn内置的Int32符号,而非你自定义的那个,这是转换的关键。 - 运算符名称映射:使用
WellKnownMemberNames中的常量来避免硬编码运算符的元数据名称,保证兼容性。 - 参数校验:通过检查参数数量和类型,确保找到的是我们需要的那个运算符重载(对于
Int32的比较运算符来说,通常只有一个符合条件的重载)。
这样处理后,当你调用这个方法获取自定义运算符声明处的符号时,返回的就会和代码中使用该运算符时获取的SynthesizedIntrinsicOperatorSymbol完全一致了。
内容的提问来源于stack exchange,提问作者trinalbadger587
相关产品推荐
相关产品推荐

