Files
Fishing2Server/Fantasy/Fantasy.Net/Fantasy.SourceGenerator/Common/RoslynExtensions.cs
2025-10-29 17:59:43 +08:00

135 lines
4.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Linq;
namespace Fantasy.SourceGenerator.Common
{
/// <summary>
/// Roslyn 相关的扩展方法
/// </summary>
internal static class RoslynExtensions
{
/// <summary>
/// 检查类型是否实现了指定的接口(通过完全限定名)
/// </summary>
public static bool ImplementsInterface(this INamedTypeSymbol typeSymbol, string interfaceFullName)
{
return typeSymbol.AllInterfaces.Any(i => i.ToDisplayString() == interfaceFullName);
}
/// <summary>
/// 检查类型是否继承自指定的基类(通过完全限定名)
/// </summary>
public static bool InheritsFrom(this INamedTypeSymbol typeSymbol, string baseTypeFullName)
{
var current = typeSymbol.BaseType;
while (current != null)
{
if (current.ToDisplayString() == baseTypeFullName)
{
return true;
}
current = current.BaseType;
}
return false;
}
/// <summary>
/// 获取类型的完全限定名(包括命名空间)
/// </summary>
public static string GetFullName(this ITypeSymbol typeSymbol, bool includeGlobal = true)
{
var displayString = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
return includeGlobal ? displayString : displayString.Replace("global::", "");
}
/// <summary>
/// 检查类型是否可以被实例化(非抽象、非接口、非泛型定义)
/// </summary>
public static bool IsInstantiable(this INamedTypeSymbol typeSymbol)
{
// 允许:具体的类(非抽象、非静态)
// 排除:抽象类、静态类、接口、开放泛型类型(如 MyClass<T> where T 未指定)
return typeSymbol is { IsAbstract: false, IsStatic: false, TypeKind: TypeKind.Class };
}
/// <summary>
/// 获取所有实现指定接口的类型
/// </summary>
public static IEnumerable<INamedTypeSymbol> GetTypesImplementingInterface(
this Compilation compilation,
string interfaceFullName)
{
var visitor = new InterfaceImplementationVisitor(interfaceFullName);
visitor.Visit(compilation.GlobalNamespace);
return visitor.ImplementingTypes;
}
/// <summary>
/// 转换为驼峰命名
/// </summary>
public static string ToCamelCase(this string name)
{
if (string.IsNullOrEmpty(name) || char.IsLower(name[0]))
{
return name;
}
return char.ToLowerInvariant(name[0]) + name.Substring(1);
}
/// <summary>
/// 访问器:查找实现特定接口的所有类型
/// </summary>
private class InterfaceImplementationVisitor : SymbolVisitor
{
private readonly string _interfaceFullName;
private readonly List<INamedTypeSymbol> _implementingTypes = new List<INamedTypeSymbol>();
public IReadOnlyList<INamedTypeSymbol> ImplementingTypes => _implementingTypes;
public InterfaceImplementationVisitor(string interfaceFullName)
{
_interfaceFullName = interfaceFullName;
}
public override void VisitNamespace(INamespaceSymbol symbol)
{
foreach (var member in symbol.GetMembers())
{
member.Accept(this);
}
}
public override void VisitNamedType(INamedTypeSymbol symbol)
{
if (symbol.IsInstantiable() && symbol.ImplementsInterface(_interfaceFullName))
{
_implementingTypes.Add(symbol);
}
// 递归访问嵌套类型
foreach (var nestedType in symbol.GetTypeMembers())
{
nestedType.Accept(this);
}
}
}
/// <summary>
/// 尝试获取泛型接口的类型参数
/// 例如IAwakeSystem&lt;PlayerEntity&gt; 返回 PlayerEntity
/// </summary>
public static ITypeSymbol? GetGenericInterfaceTypeArgument(
this INamedTypeSymbol typeSymbol,
string genericInterfaceName)
{
var matchingInterface = typeSymbol.AllInterfaces.FirstOrDefault(i =>
i.IsGenericType &&
i.ConstructedFrom.ToDisplayString() == genericInterfaceName);
return matchingInterface?.TypeArguments.FirstOrDefault();
}
}
}