using System.Collections.Immutable;
using Dpz.Core.SourceGenerator.Models;
using Microsoft.CodeAnalysis;

namespace Dpz.Core.SourceGenerator;

/// <summary>
/// 从服务接口读取生成装饰器所需的完整契约。
/// </summary>
internal static class InterfaceContractInspector
{
    internal static ImmutableArray<InterfaceMethod> GetMethods(INamedTypeSymbol interfaceType)
    {
        return
        [
            .. GetMembers(interfaceType)
                .OfType<IMethodSymbol>()
                .Where(method => method.MethodKind == MethodKind.Ordinary)
                .Select(method => new InterfaceMethod(
                    method.Name,
                    SymbolDisplay.ToFullyQualifiedTypeName(method.ReturnType),
                    GetTypeParameterList(method),
                    GetConstraintClauses(method),
                    [
                        .. method.Parameters.Select(parameter => new CachedParameter(
                            parameter.Name,
                            SymbolDisplay.ToFullyQualifiedTypeName(parameter.Type),
                            parameter.RefKind,
                            parameter.HasExplicitDefaultValue,
                            SymbolDisplay.ToDefaultValueLiteral(parameter),
                            parameter.IsParams
                        )),
                    ]
                )),
        ];
    }

    internal static ImmutableArray<InterfaceProperty> GetProperties(INamedTypeSymbol interfaceType)
    {
        return
        [
            .. GetMembers(interfaceType)
                .OfType<IPropertySymbol>()
                .Where(property => !property.IsStatic)
                .Select(property => new InterfaceProperty(
                    property.Name,
                    SymbolDisplay.ToFullyQualifiedTypeName(property.Type),
                    property.GetMethod is not null,
                    property.SetMethod is not null
                )),
        ];
    }

    internal static ImmutableArray<InterfaceEvent> GetEvents(INamedTypeSymbol interfaceType)
    {
        return
        [
            .. GetMembers(interfaceType)
                .OfType<IEventSymbol>()
                .Where(@event => !@event.IsStatic)
                .Select(@event => new InterfaceEvent(
                    @event.Name,
                    SymbolDisplay.ToFullyQualifiedTypeName(@event.Type)
                )),
        ];
    }

    private static IEnumerable<ISymbol> GetMembers(INamedTypeSymbol interfaceType)
    {
        foreach (
            var inheritedInterface in interfaceType.AllInterfaces.OrderBy(
                type => type.ToDisplayString(),
                StringComparer.Ordinal
            )
        )
        {
            foreach (var member in inheritedInterface.GetMembers())
            {
                yield return member;
            }
        }

        foreach (var member in interfaceType.GetMembers())
        {
            yield return member;
        }
    }

    private static string GetTypeParameterList(IMethodSymbol method)
    {
        if (method.TypeParameters.Length == 0)
        {
            return string.Empty;
        }

        return "<"
            + string.Join(", ", method.TypeParameters.Select(parameter => parameter.Name))
            + ">";
    }

    private static string GetConstraintClauses(IMethodSymbol method)
    {
        if (method.TypeParameters.Length == 0)
        {
            return string.Empty;
        }

        return string.Join(
            string.Empty,
            method.TypeParameters.Select(GetConstraintClause).Where(clause => clause.Length > 0)
        );
    }

    private static string GetConstraintClause(ITypeParameterSymbol typeParameter)
    {
        var constraints = new List<string>();
        if (typeParameter.HasUnmanagedTypeConstraint)
        {
            constraints.Add("unmanaged");
        }
        else if (typeParameter.HasValueTypeConstraint)
        {
            constraints.Add("struct");
        }
        else if (typeParameter.HasReferenceTypeConstraint)
        {
            constraints.Add("class");
        }
        else if (typeParameter.HasNotNullConstraint)
        {
            constraints.Add("notnull");
        }

        constraints.AddRange(
            typeParameter.ConstraintTypes.Select(SymbolDisplay.ToFullyQualifiedTypeName)
        );

        if (typeParameter.HasConstructorConstraint)
        {
            constraints.Add("new()");
        }

        if (constraints.Count == 0)
        {
            return string.Empty;
        }

        return " where " + typeParameter.Name + " : " + string.Join(", ", constraints);
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个用于 C# 源代码生成器的实用工具类,专门用于检查和提取接口的契约信息。它使用 Roslyn API 来分析接口类型并提取其成员信息。

主要功能

1. GetMethods - 提取接口方法

internal static ImmutableArray<InterfaceMethod> GetMethods(INamedTypeSymbol interfaceType)
  • 获取接口中所有普通方法(排除构造函数、操作符重载等特殊方法)
  • 提取每个方法的:
    • 方法名
    • 完全限定的返回类型
    • 泛型类型参数列表
    • 泛型约束子句
    • 参数列表(包括参数名、类型、ref/out/in修饰符、默认值、params修饰符)

2. GetProperties - 提取接口属性

internal static ImmutableArray<InterfaceProperty> GetProperties(INamedTypeSymbol interfaceType)
  • 获取所有非静态属性
  • 提取每个属性的:
    • 属性名
    • 完全限定类型
    • 是否有getter
    • 是否有setter

3. GetEvents - 提取接口事件

internal static ImmutableArray<InterfaceEvent> GetEvents(INamedTypeSymbol interfaceType)
  • 获取所有非静态事件
  • 提取事件名和完全限定的事件处理器类型

辅助方法

GetMembers - 递归获取所有成员

  • 先遍历所有继承的接口(按字符串顺序排序)
  • 再遍历当前接口的直接成员
  • 这样可以收集接口及其所有基接口的成员

GetTypeParameterList - 生成泛型参数列表

  • 例如:<T, U> 或空字符串

GetConstraintClauses - 生成泛型约束

  • 例如: where T : class, IDisposable where U : struct

GetConstraintClause - 生成单个类型参数的约束

按优先级处理约束:

  1. unmanaged (非托管类型)
  2. struct (值类型)
  3. class (引用类型)
  4. notnull (不可为空)
  5. 类型约束(接口或基类)
  6. new() (构造函数约束)

使用场景

这个类通常用于生成装饰器模式代码,例如:

  • 自动生成代理类
  • 创建拦截器
  • 实现 AOP(面向切面编程)

通过完整提取接口契约,可以自动生成实现相同接口的包装类,保持完整的类型信息和约束。

评论加载中...