Skip to content

实战示例和最佳实践

简介

本文档提供类型和方法符号的完整实战示例、真实使用场景、最佳实践和常见陷阱。

适合人群: 中级开发者
预计阅读时间: 40 分钟
前置知识: 类型系统、方法分析


📋 本文导航

章节难度阅读时间链接
完整示例:类型分析器🟡15 分钟查看
真实使用场景🟡15 分钟查看
最佳实践🟢10 分钟查看
反模式和陷阱🟢10 分钟查看

返回: 主文档


🟡 完整示例:类型分析器

让我们创建一个完整的类型分析器,展示如何综合使用类型和方法符号。

点击查看完整示例:类型分析器
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// 完整的类型分析器示例
/// </summary>
public class TypeAnalyzer
{
    private readonly SemanticModel _semanticModel;
    
    public TypeAnalyzer(SemanticModel semanticModel)
    {
        _semanticModel = semanticModel;
    }
    
    /// <summary>
    /// 分析类型的完整信息
    /// </summary>
    public TypeAnalysisResult AnalyzeType(INamedTypeSymbol typeSymbol)
    {
        var result = new TypeAnalysisResult
        {
            TypeName = typeSymbol.Name,
            FullName = typeSymbol.ToDisplayString(),
            TypeKind = typeSymbol.TypeKind,
            IsAbstract = typeSymbol.IsAbstract,
            IsSealed = typeSymbol.IsSealed,
            IsStatic = typeSymbol.IsStatic,
            IsRecord = typeSymbol.IsRecord
        };
        
        // 分析继承关系
        result.BaseTypes = GetBaseTypes(typeSymbol).ToList();
        result.Interfaces = typeSymbol.AllInterfaces
            .Select(i => i.ToDisplayString())
            .ToList();
        
        // 分析成员
        result.Properties = AnalyzeProperties(typeSymbol);
        result.Methods = AnalyzeMethods(typeSymbol);
        result.Fields = AnalyzeFields(typeSymbol);
        
        // 分析泛型
        if (typeSymbol.IsGenericType)
        {
            result.IsGeneric = true;
            result.TypeParameters = typeSymbol.TypeParameters
                .Select(tp => tp.Name)
                .ToList();
        }
        
        return result;
    }
    
    private IEnumerable<string> GetBaseTypes(INamedTypeSymbol typeSymbol)
    {
        var current = typeSymbol.BaseType;
        while (current != null && current.SpecialType != SpecialType.System_Object)
        {
            yield return current.ToDisplayString();
            current = current.BaseType;
        }
    }
    
    private List<PropertyInfo> AnalyzeProperties(INamedTypeSymbol typeSymbol)
    {
        return typeSymbol.GetMembers()
            .OfType<IPropertySymbol>()
            .Select(p => new PropertyInfo
            {
                Name = p.Name,
                Type = p.Type.ToDisplayString(),
                IsReadOnly = p.SetMethod == null,
                IsStatic = p.IsStatic,
                Accessibility = p.DeclaredAccessibility.ToString()
            })
            .ToList();
    }
    
    private List<MethodInfo> AnalyzeMethods(INamedTypeSymbol typeSymbol)
    {
        return typeSymbol.GetMembers()
            .OfType<IMethodSymbol>()
            .Where(m => m.MethodKind == MethodKind.Ordinary)
            .Select(m => new MethodInfo
            {
                Name = m.Name,
                ReturnType = m.ReturnType.ToDisplayString(),
                Parameters = m.Parameters
                    .Select(p => $"{p.Type.ToDisplayString()} {p.Name}")
                    .ToList(),
                IsStatic = m.IsStatic,
                IsAsync = m.IsAsync,
                IsVirtual = m.IsVirtual,
                IsOverride = m.IsOverride,
                Accessibility = m.DeclaredAccessibility.ToString()
            })
            .ToList();
    }
    
    private List<FieldInfo> AnalyzeFields(INamedTypeSymbol typeSymbol)
    {
        return typeSymbol.GetMembers()
            .OfType<IFieldSymbol>()
            .Where(f => !f.IsImplicitlyDeclared)
            .Select(f => new FieldInfo
            {
                Name = f.Name,
                Type = f.Type.ToDisplayString(),
                IsReadOnly = f.IsReadOnly,
                IsStatic = f.IsStatic,
                IsConst = f.IsConst,
                Accessibility = f.DeclaredAccessibility.ToString()
            })
            .ToList();
    }
}

// 结果类
public class TypeAnalysisResult
{
    public string TypeName { get; set; }
    public string FullName { get; set; }
    public TypeKind TypeKind { get; set; }
    public bool IsAbstract { get; set; }
    public bool IsSealed { get; set; }
    public bool IsStatic { get; set; }
    public bool IsRecord { get; set; }
    public bool IsGeneric { get; set; }
    
    public List<string> BaseTypes { get; set; } = new();
    public List<string> Interfaces { get; set; } = new();
    public List<string> TypeParameters { get; set; } = new();
    
    public List<PropertyInfo> Properties { get; set; } = new();
    public List<MethodInfo> Methods { get; set; } = new();
    public List<FieldInfo> Fields { get; set; } = new();
}

public class PropertyInfo
{
    public string Name { get; set; }
    public string Type { get; set; }
    public bool IsReadOnly { get; set; }
    public bool IsStatic { get; set; }
    public string Accessibility { get; set; }
}

public class MethodInfo
{
    public string Name { get; set; }
    public string ReturnType { get; set; }
    public List<string> Parameters { get; set; } = new();
    public bool IsStatic { get; set; }
    public bool IsAsync { get; set; }
    public bool IsVirtual { get; set; }
    public bool IsOverride { get; set; }
    public string Accessibility { get; set; }
}

public class FieldInfo
{
    public string Name { get; set; }
    public string Type { get; set; }
    public bool IsReadOnly { get; set; }
    public bool IsStatic { get; set; }
    public bool IsConst { get; set; }
    public string Accessibility { get; set; }
}

关键要点:

  • 使用 GetMembers() 获取所有成员,然后使用 LINQ 过滤
  • 使用 OfType<T>() 过滤特定类型的符号
  • 使用 ToDisplayString() 获取类型的友好显示名称
  • 检查 IsImplicitlyDeclared 过滤编译器生成的成员

🟡 真实使用场景

场景 1:查找派生类

查找所有派生自特定基类的类型。

csharp
public class DerivedClassFinder
{
    public List<INamedTypeSymbol> FindDerivedClasses(
        Compilation compilation,
        INamedTypeSymbol baseType)
    {
        var derivedClasses = new List<INamedTypeSymbol>();
        
        // 遍历所有类型
        foreach (var syntaxTree in compilation.SyntaxTrees)
        {
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var root = syntaxTree.GetRoot();
            
            var classDeclarations = root.DescendantNodes()
                .OfType<ClassDeclarationSyntax>();
            
            foreach (var classDecl in classDeclarations)
            {
                var classSymbol = semanticModel.GetDeclaredSymbol(classDecl);
                if (classSymbol == null) continue;
                
                // 检查是否派生自基类
                if (IsDerivedFrom(classSymbol, baseType))
                {
                    derivedClasses.Add(classSymbol);
                }
            }
        }
        
        return derivedClasses;
    }
    
    private bool IsDerivedFrom(INamedTypeSymbol type, INamedTypeSymbol baseType)
    {
        var current = type.BaseType;
        while (current != null)
        {
            if (SymbolEqualityComparer.Default.Equals(current, baseType))
                return true;
            current = current.BaseType;
        }
        return false;
    }
}

场景 2:分析接口实现

检查类型是否实现了特定接口,并分析接口成员的实现。

csharp
public class InterfaceImplementationAnalyzer
{
    public void AnalyzeInterfaceImplementation(
        INamedTypeSymbol typeSymbol,
        INamedTypeSymbol interfaceSymbol)
    {
        // 检查是否实现了接口
        if (!typeSymbol.AllInterfaces.Contains(interfaceSymbol, SymbolEqualityComparer.Default))
        {
            Console.WriteLine($"{typeSymbol.Name} 没有实现 {interfaceSymbol.Name}");
            return;
        }
        
        Console.WriteLine($"{typeSymbol.Name} 实现了 {interfaceSymbol.Name}");
        Console.WriteLine("\n接口成员实现:");
        
        // 分析每个接口成员的实现
        foreach (var interfaceMember in interfaceSymbol.GetMembers())
        {
            var implementation = typeSymbol.FindImplementationForInterfaceMember(interfaceMember);
            
            if (implementation != null)
            {
                Console.WriteLine($"  {interfaceMember.Name}:");
                Console.WriteLine($"    实现: {implementation.ToDisplayString()}");
                Console.WriteLine($"    定义在: {implementation.ContainingType.Name}");
            }
            else
            {
                Console.WriteLine($"  {interfaceMember.Name}: 未找到实现");
            }
        }
    }
}

场景 3:生成类型代码

根据类型符号生成代码。

点击查看完整示例:代码生成器
csharp
public class CodeGenerator
{
    public string GenerateDto(INamedTypeSymbol typeSymbol)
    {
        var sb = new StringBuilder();
        
        // 生成类声明
        sb.AppendLine($"public class {typeSymbol.Name}Dto");
        sb.AppendLine("{");
        
        // 生成属性
        var properties = typeSymbol.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => p.DeclaredAccessibility == Accessibility.Public);
        
        foreach (var prop in properties)
        {
            var typeName = prop.Type.ToDisplayString();
            sb.AppendLine($"    public {typeName} {prop.Name} {{ get; set; }}");
        }
        
        sb.AppendLine("}");
        
        return sb.ToString();
    }
    
    public string GenerateMapper(INamedTypeSymbol sourceType, INamedTypeSymbol targetType)
    {
        var sb = new StringBuilder();
        
        sb.AppendLine($"public static {targetType.Name} MapTo{targetType.Name}({sourceType.Name} source)");
        sb.AppendLine("{");
        sb.AppendLine($"    return new {targetType.Name}");
        sb.AppendLine("    {");
        
        // 查找匹配的属性
        var sourceProps = sourceType.GetMembers()
            .OfType<IPropertySymbol>()
            .ToDictionary(p => p.Name);
        
        var targetProps = targetType.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => p.SetMethod != null);
        
        var mappings = new List<string>();
        foreach (var targetProp in targetProps)
        {
            if (sourceProps.TryGetValue(targetProp.Name, out var sourceProp))
            {
                // 检查类型是否兼容
                if (SymbolEqualityComparer.Default.Equals(sourceProp.Type, targetProp.Type))
                {
                    mappings.Add($"        {targetProp.Name} = source.{sourceProp.Name}");
                }
            }
        }
        
        sb.AppendLine(string.Join(",\n", mappings));
        sb.AppendLine("    };");
        sb.AppendLine("}");
        
        return sb.ToString();
    }
}

关键要点:

  • 使用 GetMembers() 和 LINQ 过滤需要的成员
  • 使用 ToDisplayString() 生成类型名称
  • 使用 SymbolEqualityComparer 比较类型
  • 检查属性的 SetMethod 判断是否可写

🟢 最佳实践

1. 使用 SymbolEqualityComparer 比较符号

错误做法:

csharp
if (type1 == type2) // 不要这样做

正确做法:

csharp
if (SymbolEqualityComparer.Default.Equals(type1, type2))

2. 检查泛型类型时使用 OriginalDefinition

错误做法:

csharp
if (typeSymbol.Name == "List") // 不准确

正确做法:

csharp
var listType = compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");
if (SymbolEqualityComparer.Default.Equals(
    typeSymbol.OriginalDefinition, listType))

3. 处理可空引用类型

csharp
public void HandleNullableReferenceType(ITypeSymbol typeSymbol)
{
    // 检查是否是可空引用类型
    if (typeSymbol.NullableAnnotation == NullableAnnotation.Annotated)
    {
        Console.WriteLine("这是可空引用类型");
    }
    
    // 获取非可空版本
    var nonNullableType = typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated);
}

4. 遍历继承链时检查 null

csharp
public void TraverseInheritanceChain(INamedTypeSymbol typeSymbol)
{
    var current = typeSymbol.BaseType;
    while (current != null) // 始终检查 null
    {
        Console.WriteLine(current.Name);
        current = current.BaseType;
    }
}

5. 使用 GetTypeMembers 而不是过滤 GetMembers

低效做法:

csharp
var nestedTypes = typeSymbol.GetMembers()
    .OfType<INamedTypeSymbol>();

高效做法:

csharp
var nestedTypes = typeSymbol.GetTypeMembers();

🟢 反模式和陷阱

反模式 1:使用字符串比较类型

错误示例:

csharp
if (typeSymbol.Name == "List")
{
    // 这会匹配所有名为 List 的类型,不仅仅是 System.Collections.Generic.List
}

正确示例:

csharp
var listType = compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");
if (SymbolEqualityComparer.Default.Equals(
    typeSymbol.OriginalDefinition, listType))
{
    // 正确匹配 System.Collections.Generic.List<T>
}

反模式 2:忽略泛型类型的 OriginalDefinition

错误示例:

csharp
// List<int> 和 List<string> 会被认为是不同的类型
if (SymbolEqualityComparer.Default.Equals(listOfInt, listOfString))
{
    // 永远不会执行
}

正确示例:

csharp
// 比较泛型定义
if (SymbolEqualityComparer.Default.Equals(
    listOfInt.OriginalDefinition,
    listOfString.OriginalDefinition))
{
    // 正确:都是 List<T>
}

反模式 3:不检查 null 值

错误示例:

csharp
var baseType = typeSymbol.BaseType;
Console.WriteLine(baseType.Name); // 可能抛出 NullReferenceException

正确示例:

csharp
var baseType = typeSymbol.BaseType;
if (baseType != null)
{
    Console.WriteLine(baseType.Name);
}

反模式 4:过度使用反射式的字符串操作

错误示例:

csharp
var typeName = typeSymbol.ToDisplayString();
if (typeName.Contains("List"))
{
    // 不可靠的类型检查
}

正确示例:

csharp
// 使用符号比较
var listType = compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");
if (typeSymbol.OriginalDefinition.Equals(listType, SymbolEqualityComparer.Default))
{
    // 可靠的类型检查
}

注意

始终使用符号比较而不是字符串比较,这样更准确、更高效。


📚 相关资源


📚 下一步

基于 MIT 许可发布