方法分析详解
简介
本文档详细介绍 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 修饰符。