Skip to content

语义模型 API

深入学习 Roslyn 语义模型 API 的使用方法

📚 本文档内容

本文档详细介绍语义模型 API 的使用,包括:

  • 语义模型的获取和使用
  • 符号类型和常用属性
  • 特性处理
  • 编译 API
  • 代码生成 API
  • 诊断 API

📋 文档信息

项目信息
文档标题语义模型 API
所属系列学习指南
难度级别⭐⭐⭐⭐ 高级
预计阅读时间60 分钟
前置知识语法树 API
相关文档Roslyn API 介绍常见模式

🎯 学习目标

学完本文档后,你将能够:

  • ✅ 理解语义模型的作用和使用场景
  • ✅ 掌握符号信息的获取方法
  • ✅ 熟练处理特性数据
  • ✅ 理解编译 API 的使用
  • ✅ 掌握代码生成技巧
  • ✅ 能够创建和报告诊断信息

📑 快速导航

章节内容概要跳转链接
语义模型 API获取符号信息和类型信息查看
符号类型各种符号类型的使用查看
特性处理处理特性数据查看
编译 API创建和操作编译查看
代码生成使用 SourceText 和 SyntaxFactory查看
诊断 API创建和报告诊断查看

概览

语义模型提供代码的语义信息,包括类型信息、符号解析、类型检查等。它建立在语法树之上,提供更深层次的代码理解。

语义模型 API

获取符号信息

csharp
// 从语义模型获取符号
SemanticModel semanticModel = compilation.GetSemanticModel(syntaxTree);

// 获取声明的符号
ISymbol symbol = semanticModel.GetDeclaredSymbol(syntaxNode);

// 获取类型符号
INamedTypeSymbol classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration);

// 获取方法符号
IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration);

// 获取属性符号
IPropertySymbol propertySymbol = semanticModel.GetDeclaredSymbol(propertyDeclaration);

// 获取字段符号
IFieldSymbol fieldSymbol = semanticModel.GetDeclaredSymbol(variableDeclarator);

// 获取表达式的类型信息
TypeInfo typeInfo = semanticModel.GetTypeInfo(expression);
ITypeSymbol type = typeInfo.Type;
ITypeSymbol convertedType = typeInfo.ConvertedType;

// 获取符号信息(用于引用)
SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression);
ISymbol referencedSymbol = symbolInfo.Symbol;

符号类型和常用属性

INamedTypeSymbol - 命名类型(类、接口、结构等)

csharp
INamedTypeSymbol typeSymbol;
string typeName = typeSymbol.Name;
string fullName = typeSymbol.ToDisplayString();
TypeKind kind = typeSymbol.TypeKind; // Class, Interface, Struct, Enum, Delegate
INamespaceSymbol containingNamespace = typeSymbol.ContainingNamespace;
INamedTypeSymbol baseType = typeSymbol.BaseType;
ImmutableArray<INamedTypeSymbol> interfaces = typeSymbol.Interfaces;
ImmutableArray<ISymbol> members = typeSymbol.GetMembers();
ImmutableArray<IMethodSymbol> methods = typeSymbol.GetMembers().OfType<IMethodSymbol>();
ImmutableArray<IPropertySymbol> properties = typeSymbol.GetMembers().OfType<IPropertySymbol>();

IMethodSymbol - 方法

csharp
IMethodSymbol methodSymbol;
string methodName = methodSymbol.Name;
ITypeSymbol returnType = methodSymbol.ReturnType;
ImmutableArray<IParameterSymbol> parameters = methodSymbol.Parameters;
bool isStatic = methodSymbol.IsStatic;
bool isAsync = methodSymbol.IsAsync;
bool isExtensionMethod = methodSymbol.IsExtensionMethod;
MethodKind methodKind = methodSymbol.MethodKind; // Ordinary, Constructor, PropertyGet, etc.

IPropertySymbol - 属性

csharp
IPropertySymbol propertySymbol;
string propertyName = propertySymbol.Name;
ITypeSymbol propertyType = propertySymbol.Type;
IMethodSymbol getMethod = propertySymbol.GetMethod;
IMethodSymbol setMethod = propertySymbol.SetMethod;
bool isReadOnly = propertySymbol.IsReadOnly;
bool isWriteOnly = propertySymbol.IsWriteOnly;

IFieldSymbol - 字段

csharp
IFieldSymbol fieldSymbol;
string fieldName = fieldSymbol.Name;
ITypeSymbol fieldType = fieldSymbol.Type;
bool isReadOnly = fieldSymbol.IsReadOnly;
bool isConst = fieldSymbol.IsConst;
object constantValue = fieldSymbol.ConstantValue;

通用符号属性

csharp
ISymbol symbol;
string name = symbol.Name;
Accessibility accessibility = symbol.DeclaredAccessibility; // Public, Private, Protected, Internal
bool isStatic = symbol.IsStatic;
bool isVirtual = symbol.IsVirtual;
bool isAbstract = symbol.IsAbstract;
bool isSealed = symbol.IsSealed;
ImmutableArray<AttributeData> attributes = symbol.GetAttributes();

特性 (Attribute) 处理

csharp
// 获取符号的所有特性
ImmutableArray<AttributeData> attributes = symbol.GetAttributes();

// 查找特定特性
AttributeData myAttribute = attributes.FirstOrDefault(a => 
    a.AttributeClass?.Name == "MyAttribute");

// 检查是否有特定特性
bool hasAttribute = attributes.Any(a => 
    a.AttributeClass?.ToDisplayString() == "Namespace.MyAttribute");

// 获取特性参数
if (myAttribute != null)
{
    // 构造函数参数
    ImmutableArray<TypedConstant> constructorArgs = myAttribute.ConstructorArguments;
    object firstArg = constructorArgs[0].Value;
    
    // 命名参数
    ImmutableArray<KeyValuePair<string, TypedConstant>> namedArgs = myAttribute.NamedArguments;
    foreach (var arg in namedArgs)
    {
        string paramName = arg.Key;
        object paramValue = arg.Value.Value;
    }
}

编译 API

创建和操作编译

csharp
// 创建编译
CSharpCompilation compilation = CSharpCompilation.Create(
    assemblyName: "MyAssembly",
    syntaxTrees: new[] { syntaxTree },
    references: new[] { 
        MetadataReference.CreateFromFile(typeof(object).Assembly.Location)
    },
    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

// 添加语法树
compilation = compilation.AddSyntaxTrees(newTree);

// 获取所有语法树
ImmutableArray<SyntaxTree> trees = compilation.SyntaxTrees;

// 获取程序集符号
IAssemblySymbol assemblySymbol = compilation.Assembly;

// 获取全局命名空间
INamespaceSymbol globalNamespace = compilation.GlobalNamespace;

// 查找类型
INamedTypeSymbol typeSymbol = compilation.GetTypeByMetadataName("Namespace.ClassName");

// 获取特殊类型
INamedTypeSymbol objectType = compilation.GetSpecialType(SpecialType.System_Object);
INamedTypeSymbol stringType = compilation.GetSpecialType(SpecialType.System_String);
INamedTypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32);

// 类型转换检查
Conversion conversion = compilation.ClassifyConversion(sourceType, targetType);
bool isImplicit = conversion.IsImplicit;
bool isExplicit = conversion.IsExplicit;

代码生成 API

使用 SourceText

csharp
using Microsoft.CodeAnalysis.Text;

// 从字符串创建
SourceText sourceText = SourceText.From(code, Encoding.UTF8);

// 添加到生成器上下文
context.AddSource("FileName.g.cs", sourceText);

// 或直接使用字符串
context.AddSource("FileName.g.cs", code);

使用 SyntaxFactory 构建语法树

csharp
using Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

// 创建类声明
ClassDeclarationSyntax classDecl = ClassDeclaration("MyClass")
    .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.PartialKeyword))
    .AddMembers(
        PropertyDeclaration(
            PredefinedType(Token(SyntaxKind.StringKeyword)),
            "Name")
        .AddModifiers(Token(SyntaxKind.PublicKeyword))
        .AddAccessorListAccessors(
            AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
                .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
            AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
                .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))));

// 创建命名空间
NamespaceDeclarationSyntax namespaceDecl = NamespaceDeclaration(
    IdentifierName("MyNamespace"))
    .AddMembers(classDecl);

// 创建编译单元
CompilationUnitSyntax compilationUnit = CompilationUnit()
    .AddUsings(UsingDirective(IdentifierName("System")))
    .AddMembers(namespaceDecl)
    .NormalizeWhitespace();

// 转换为字符串
string code = compilationUnit.ToFullString();

诊断 API

创建和报告诊断

csharp
// 定义诊断描述符
DiagnosticDescriptor descriptor = new DiagnosticDescriptor(
    id: "SG0001",
    title: "Invalid usage",
    messageFormat: "The class '{0}' must be partial",
    category: "SourceGenerator",
    defaultSeverity: DiagnosticSeverity.Error,
    isEnabledByDefault: true,
    description: "Classes using this attribute must be declared as partial.");

// 创建诊断
Diagnostic diagnostic = Diagnostic.Create(
    descriptor,
    location: node.GetLocation(),
    messageArgs: className);

// 报告诊断
context.ReportDiagnostic(diagnostic);

// 诊断严重级别
DiagnosticSeverity.Error   // 编译错误
DiagnosticSeverity.Warning // 警告
DiagnosticSeverity.Info    // 信息
DiagnosticSeverity.Hidden  // 隐藏(IDE 提示)

增量生成器管道 API

管道操作

csharp
// Select - 转换数据
IncrementalValuesProvider<string> names = provider
    .Select(static (data, _) => data.Name);

// Where - 过滤数据
IncrementalValuesProvider<ClassInfo> filtered = provider
    .Where(static data => data.IsValid);

// Combine - 组合两个提供者
IncrementalValueProvider<(ClassInfo, ConfigInfo)> combined = 
    classProvider.Combine(configProvider);

// Collect - 收集所有值到集合
IncrementalValueProvider<ImmutableArray<ClassInfo>> collected = 
    provider.Collect();

// SelectMany - 展平集合
IncrementalValuesProvider<PropertyInfo> properties = provider
    .SelectMany(static (classInfo, _) => classInfo.Properties);

ForAttributeWithMetadataName (.NET 7+)

csharp
// 高性能的基于特性的查找
var provider = context.SyntaxProvider
    .ForAttributeWithMetadataName(
        fullyQualifiedMetadataName: "MyNamespace.MyAttribute",
        predicate: static (node, _) => node is ClassDeclarationSyntax,
        transform: static (context, _) => {
            var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
            var attributeData = context.Attributes[0];
            return new ClassInfo(classSymbol, attributeData);
        });

💡 关键要点

  1. 语义模型提供深层信息

    • 类型信息、符号解析、类型检查
    • 比语法分析慢,但提供更多信息
  2. 符号是语义的核心

    • ISymbol 表示命名实体
    • 提供类型、可访问性、修饰符等信息
  3. 特性处理

    • 使用 GetAttributes() 获取特性
    • 区分构造函数参数和命名参数
  4. 编译 API

    • 管理语法树和引用
    • 提供类型查找和转换检查
  5. 代码生成

    • 使用 SourceTextSyntaxFactory
    • SyntaxFactory 更类型安全但更复杂
  6. 诊断报告

    • 使用 DiagnosticDescriptor 定义诊断
    • 选择合适的严重级别

🔗 相关文档


📚 下一步

学习完语义模型 API 后,建议继续学习:

  1. 常见模式 - 学习实用的代码模式
  2. 最佳实践 - 了解性能优化方法
  3. 故障排除 - 解决常见问题

基于 MIT 许可发布