实战示例和最佳实践
简介
本文档提供类型和方法符号的完整实战示例、真实使用场景、最佳实践和常见陷阱。
适合人群: 中级开发者
预计阅读时间: 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))
{
// 可靠的类型检查
}注意
始终使用符号比较而不是字符串比较,这样更准确、更高效。