Skip to content

方法分析详解

简介

本文档详细介绍 IMethodSymbol(方法符号),包括方法签名、参数处理、方法重载和重写等内容。

适合人群: 中级开发者
预计阅读时间: 45 分钟
前置知识: 语法树基础、符号系统、类型系统


📋 本文导航

章节难度阅读时间链接
核心属性🟢10 分钟查看
方法签名分析🟡15 分钟查看
参数和返回值🟢10 分钟查看
方法重载和重写🟡10 分钟查看
扩展方法🟢5 分钟查看

返回: 主文档 | 上一篇: 类型系统详解


🟢 核心属性

IMethodSymbol 表示方法、构造函数、析构函数、运算符等可调用的成员。

基本信息属性

csharp
IMethodSymbol methodSymbol;

// 方法名称
string name = methodSymbol.Name;

// 方法种类(Ordinary, Constructor, Destructor, Operator 等)
MethodKind methodKind = methodSymbol.MethodKind;

// 返回类型
ITypeSymbol returnType = methodSymbol.ReturnType;

// 参数列表
ImmutableArray<IParameterSymbol> parameters = methodSymbol.Parameters;

// 访问修饰符
Accessibility accessibility = methodSymbol.DeclaredAccessibility;

// 是否是静态方法
bool isStatic = methodSymbol.IsStatic;

// 是否是抽象方法
bool isAbstract = methodSymbol.IsAbstract;

// 是否是虚方法
bool isVirtual = methodSymbol.IsVirtual;

// 是否是重写方法
bool isOverride = methodSymbol.IsOverride;

// 是否是密封方法
bool isSealed = methodSymbol.IsSealed;

特殊方法属性

csharp
// 是否是异步方法
bool isAsync = methodSymbol.IsAsync;

// 是否是扩展方法
bool isExtensionMethod = methodSymbol.IsExtensionMethod;

// 是否是泛型方法
bool isGenericMethod = methodSymbol.IsGenericMethod;

// 是否是部分方法
bool isPartialMethod = methodSymbol.IsPartialDefinition || methodSymbol.IsPartialImplementation;

// 是否是外部方法
bool isExtern = methodSymbol.IsExtern;

// 是否是条件方法
bool isConditional = methodSymbol.GetAttributes()
    .Any(a => a.AttributeClass?.Name == "ConditionalAttribute");

示例:分析方法基本信息

csharp
public void AnalyzeMethodBasicInfo(IMethodSymbol methodSymbol)
{
    Console.WriteLine($"方法名称: {methodSymbol.Name}");
    Console.WriteLine($"方法种类: {methodSymbol.MethodKind}");
    Console.WriteLine($"返回类型: {methodSymbol.ReturnType.ToDisplayString()}");
    Console.WriteLine($"参数数量: {methodSymbol.Parameters.Length}");
    Console.WriteLine($"访问修饰符: {methodSymbol.DeclaredAccessibility}");
    
    // 检查方法特性
    if (methodSymbol.IsStatic)
        Console.WriteLine("这是一个静态方法");
    if (methodSymbol.IsAsync)
        Console.WriteLine("这是一个异步方法");
    if (methodSymbol.IsExtensionMethod)
        Console.WriteLine("这是一个扩展方法");
    if (methodSymbol.IsGenericMethod)
        Console.WriteLine("这是一个泛型方法");
    
    // 列出所有参数
    Console.WriteLine("\n参数列表:");
    foreach (var param in methodSymbol.Parameters)
    {
        Console.WriteLine($"  - {param.Type.ToDisplayString()} {param.Name}");
    }
}

🟡 方法签名分析

方法签名包括方法名称、参数类型和数量,是方法重载的关键。

方法签名组成

csharp
public void AnalyzeMethodSignature(IMethodSymbol methodSymbol)
{
    // 1. 方法名称
    Console.WriteLine($"方法名称: {methodSymbol.Name}");
    
    // 2. 返回类型
    Console.WriteLine($"返回类型: {methodSymbol.ReturnType.ToDisplayString()}");
    
    // 3. 参数签名
    Console.WriteLine("参数签名:");
    foreach (var param in methodSymbol.Parameters)
    {
        var refKind = param.RefKind switch
        {
            RefKind.None => "",
            RefKind.Ref => "ref ",
            RefKind.Out => "out ",
            RefKind.In => "in ",
            _ => ""
        };
        
        Console.WriteLine($"  - {refKind}{param.Type.ToDisplayString()} {param.Name}");
    }
    
    // 4. 泛型参数
    if (methodSymbol.IsGenericMethod)
    {
        Console.WriteLine("泛型参数:");
        foreach (var typeParam in methodSymbol.TypeParameters)
        {
            Console.WriteLine($"  - {typeParam.Name}");
        }
    }
}

比较方法签名

csharp
public bool IsSameSignature(IMethodSymbol method1, IMethodSymbol method2)
{
    // 1. 检查方法名称
    if (method1.Name != method2.Name)
        return false;
    
    // 2. 检查参数数量
    if (method1.Parameters.Length != method2.Parameters.Length)
        return false;
    
    // 3. 检查每个参数的类型
    for (int i = 0; i < method1.Parameters.Length; i++)
    {
        var param1 = method1.Parameters[i];
        var param2 = method2.Parameters[i];
        
        // 比较参数类型
        if (!SymbolEqualityComparer.Default.Equals(param1.Type, param2.Type))
            return false;
        
        // 比较 ref/out/in 修饰符
        if (param1.RefKind != param2.RefKind)
            return false;
    }
    
    return true;
}

提示

方法签名不包括返回类型,只包括方法名称和参数类型。


🟢 参数和返回值

参数分析

csharp
public void AnalyzeParameters(IMethodSymbol methodSymbol)
{
    foreach (var param in methodSymbol.Parameters)
    {
        Console.WriteLine($"\n参数: {param.Name}");
        Console.WriteLine($"  类型: {param.Type.ToDisplayString()}");
        Console.WriteLine($"  引用类型: {param.RefKind}");
        
        // 检查是否有默认值
        if (param.HasExplicitDefaultValue)
        {
            Console.WriteLine($"  默认值: {param.ExplicitDefaultValue}");
        }
        
        // 检查是否是 params 参数
        if (param.IsParams)
        {
            Console.WriteLine("  这是一个 params 参数");
        }
        
        // 检查是否是可选参数
        if (param.IsOptional)
        {
            Console.WriteLine("  这是一个可选参数");
        }
    }
}

返回值分析

csharp
public void AnalyzeReturnType(IMethodSymbol methodSymbol)
{
    var returnType = methodSymbol.ReturnType;
    
    Console.WriteLine($"返回类型: {returnType.ToDisplayString()}");
    Console.WriteLine($"返回类型种类: {returnType.TypeKind}");
    
    // 检查是否返回 void
    if (returnType.SpecialType == SpecialType.System_Void)
    {
        Console.WriteLine("方法不返回值");
    }
    
    // 检查是否返回 Task 或 Task<T>
    if (methodSymbol.IsAsync)
    {
        if (returnType.Name == "Task")
        {
            var namedType = returnType as INamedTypeSymbol;
            if (namedType?.IsGenericType == true)
            {
                var resultType = namedType.TypeArguments[0];
                Console.WriteLine($"异步方法返回: {resultType.ToDisplayString()}");
            }
            else
            {
                Console.WriteLine("异步方法不返回值");
            }
        }
    }
    
    // 检查返回类型的引用类型
    Console.WriteLine($"返回引用类型: {methodSymbol.ReturnsByRef}");
    Console.WriteLine($"返回只读引用: {methodSymbol.ReturnsByRefReadonly}");
}

🟡 方法重载和重写

方法重载

方法重载是指在同一个类中定义多个同名但参数不同的方法。

csharp
public void FindOverloads(INamedTypeSymbol typeSymbol, string methodName)
{
    // 获取所有同名方法
    var methods = typeSymbol.GetMembers(methodName)
        .OfType<IMethodSymbol>()
        .ToList();
    
    Console.WriteLine($"找到 {methods.Count} 个重载:");
    
    foreach (var method in methods)
    {
        var paramList = string.Join(", ", 
            method.Parameters.Select(p => p.Type.ToDisplayString()));
        
        Console.WriteLine($"  - {method.Name}({paramList})");
    }
}

方法重写

csharp
public void AnalyzeMethodOverride(IMethodSymbol methodSymbol)
{
    if (!methodSymbol.IsOverride)
    {
        Console.WriteLine("这不是重写方法");
        return;
    }
    
    Console.WriteLine($"重写方法: {methodSymbol.ToDisplayString()}");
    
    // 获取被重写的方法
    var overriddenMethod = methodSymbol.OverriddenMethod;
    if (overriddenMethod != null)
    {
        Console.WriteLine($"被重写的方法: {overriddenMethod.ToDisplayString()}");
        Console.WriteLine($"定义在: {overriddenMethod.ContainingType.ToDisplayString()}");
    }
    
    // 获取原始虚方法
    var current = methodSymbol;
    while (current.OverriddenMethod != null)
    {
        current = current.OverriddenMethod;
    }
    
    if (current.IsVirtual)
    {
        Console.WriteLine($"原始虚方法: {current.ToDisplayString()}");
        Console.WriteLine($"定义在: {current.ContainingType.ToDisplayString()}");
    }
}
点击查看完整示例:分析方法重写链
csharp
public void AnalyzeOverrideChain(IMethodSymbol methodSymbol)
{
    Console.WriteLine($"分析方法: {methodSymbol.ToDisplayString()}");
    
    if (!methodSymbol.IsOverride && !methodSymbol.IsVirtual)
    {
        Console.WriteLine("这不是虚方法或重写方法");
        return;
    }
    
    // 构建重写链
    var chain = new List<IMethodSymbol>();
    var current = methodSymbol;
    
    // 向上追溯到原始虚方法
    while (current != null)
    {
        chain.Insert(0, current);
        current = current.OverriddenMethod;
    }
    
    // 显示重写链
    Console.WriteLine("\n重写链:");
    for (int i = 0; i < chain.Count; i++)
    {
        var method = chain[i];
        var indent = new string(' ', i * 2);
        var modifier = method.IsVirtual && !method.IsOverride ? "virtual" :
                      method.IsOverride ? "override" : "";
        
        Console.WriteLine($"{indent}{modifier} {method.ToDisplayString()}");
        Console.WriteLine($"{indent}  定义在: {method.ContainingType.ToDisplayString()}");
    }
}

关键要点:

  • 使用 IsOverride 检查是否是重写方法
  • 使用 OverriddenMethod 获取被重写的方法
  • 可以通过循环追溯到原始虚方法
  • 重写链从基类的虚方法开始,到最派生类的重写方法结束

🟢 扩展方法

扩展方法是 C# 的特殊功能,允许为现有类型添加方法。

csharp
public void AnalyzeExtensionMethod(IMethodSymbol methodSymbol)
{
    if (!methodSymbol.IsExtensionMethod)
    {
        Console.WriteLine("这不是扩展方法");
        return;
    }
    
    Console.WriteLine($"扩展方法: {methodSymbol.Name}");
    
    // 获取被扩展的类型(第一个参数的类型)
    if (methodSymbol.Parameters.Length > 0)
    {
        var extendedType = methodSymbol.Parameters[0].Type;
        Console.WriteLine($"扩展类型: {extendedType.ToDisplayString()}");
    }
    
    // 获取其他参数
    if (methodSymbol.Parameters.Length > 1)
    {
        Console.WriteLine("其他参数:");
        for (int i = 1; i < methodSymbol.Parameters.Length; i++)
        {
            var param = methodSymbol.Parameters[i];
            Console.WriteLine($"  - {param.Type.ToDisplayString()} {param.Name}");
        }
    }
    
    // 获取定义扩展方法的类
    Console.WriteLine($"定义在: {methodSymbol.ContainingType.ToDisplayString()}");
}

注意

扩展方法必须定义在静态类中,且第一个参数必须使用 this 修饰符。


🔗 相关 API


📚 下一步

基于 MIT 许可发布