You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否为单个类生成多个接口/类?SignalR Hub类场景咨询

实现单个C# Hub类生成多个TypeScript接口的方案

要实现从带[TsInterface]特性的SignalR Hub类生成你期望的三个TypeScript接口,最靠谱的方式是用**.NET源代码生成器(Source Generator)**——它能在编译时自动扫描带特性的类,生成对应的TS代码,完全匹配你的需求。

具体步骤和实现思路

1. 先定义[TsInterface]特性(如果还没定义)

首先得有这个标记特性,用来标识需要生成TS接口的Hub类:

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class TsInterfaceAttribute : Attribute
{
    // 可按需添加配置参数,比如指定TS文件输出路径等
}

2. 编写源代码生成器

创建一个.NET类库项目,引用Microsoft.CodeAnalysis.CSharp和Microsoft.CodeAnalysis.Analyzers包,然后编写生成器核心逻辑:

核心逻辑分为三步:

  • 扫描所有带[TsInterface]特性的Hub类
  • 提取类名、公共方法的元信息
  • 按模板生成三个TS接口

以下是生成器的核心代码片段:

[Generator]
public class TsInterfaceGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        // 注册特性接收器,捕获带[TsInterface]的类
        context.RegisterForSyntaxNotifications(() => new TsInterfaceSyntaxReceiver());
    }

    public void Execute(GeneratorExecutionContext context)
    {
        var receiver = context.SyntaxReceiver as TsInterfaceSyntaxReceiver;
        if (receiver?.TargetClasses == null) return;

        foreach (var classSymbol in receiver.TargetClasses)
        {
            var className = classSymbol.Name;
            var serverMethods = GetServerMethods(classSymbol);

            // 拼接生成TS代码字符串
            var tsCode = $@"
interface {className} extends SignalR.Hub.Proxy {{
    client: {className}Client;
    server: {className}Server;
}}

interface {className}Client {{
    // always empty.
}}

interface {className}Server {{
    {string.Join("\n    ", serverMethods)}
}}";

            // 将生成的代码添加到编译上下文,输出为.d.ts文件
            context.AddSource($"{className}.d.ts", SourceText.From(tsCode, Encoding.UTF8));
        }
    }

    // 提取Hub类的公共方法,转换为TS方法签名
    private IEnumerable<string> GetServerMethods(INamedTypeSymbol classSymbol)
    {
        return classSymbol.GetMembers()
            .OfType<IMethodSymbol>()
            .Where(m => m.DeclaredAccessibility == Accessibility.Public && !m.IsStatic)
            .Select(ConvertToTsMethod);
    }

    // 将C#方法转换为符合TS风格的方法签名
    private string ConvertToTsMethod(IMethodSymbol method)
    {
        // 处理返回值:Task<T> → Promise<T>,Task → Promise<void>
        var returnType = method.ReturnType.Name switch
        {
            "Task" => "Promise<void>",
            "Task`1" => 
                $"Promise<{ConvertTsType(method.ReturnType.TypeArguments[0])}>",
            _ => "Promise<any>"
        };

        // 处理参数:C#大驼峰参数名转小驼峰,加上TS类型
        var parameters = method.Parameters.Select(p => 
            $"{ToCamelCase(p.Name)}: {ConvertTsType(p.Type)}");

        // 方法名转小驼峰(符合TS编码风格)
        var methodName = ToCamelCase(method.Name);

        return $"{methodName}({string.Join(", ", parameters)}): {returnType};";
    }

    // C#类型到TS类型的基础映射(可按需扩展更多类型)
    private string ConvertTsType(ITypeSymbol type)
    {
        return type.Name switch
        {
            "String" => "string",
            "Int32" => "number",
            "IEnumerable`1" => $"{ConvertTsType(type.TypeArguments[0])}[]",
            // 可添加更多类型映射,比如DateTime转string等
            _ => "any"
        };
    }

    // 大驼峰转小驼峰工具方法
    private string ToCamelCase(string input)
    {
        if (string.IsNullOrEmpty(input)) return input;
        return char.ToLowerInvariant(input[0]) + input.Substring(1);
    }
}

// 语法接收器,收集带[TsInterface]特性的类节点
public class TsInterfaceSyntaxReceiver : ISyntaxReceiver
{
    public List<INamedTypeSymbol> TargetClasses { get; } = new();

    public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
    {
        if (syntaxNode is ClassDeclarationSyntax classDecl && 
            classDecl.AttributeLists.Any(al => al.Attributes.Any(a => 
                a.Name.ToString() == "TsInterface")))
        {
            // 在Execute阶段通过语义模型转换为INamedTypeSymbol,这里先标记需要处理的类
        }
    }
}

3. 使用生成器

将这个生成器项目作为依赖引用到你的SignalR项目中,给ChatHub加上[TsInterface]特性后编译项目,就能自动生成对应的.d.ts文件,内容和你期望的完全一致:

interface ChatHub extends SignalR.Hub.Proxy {
    client: ChatHubClient;
    server: ChatHubServer;
}

interface ChatHubClient {
    // always empty.
}

interface ChatHubServer {
    getMessages(): Promise<string[]>;
    emitMessage(msg: string): Promise<void>;
}

补充说明

  • 如果你不想自己编写生成器,也可以寻找现成的NuGet包(比如SignalR.TypeScript类库),但自定义生成器能完全把控输出格式,更贴合你的个性化需求。
  • 上面的类型映射是基础版本,你可以根据业务需求扩展更多类型转换规则,比如处理自定义实体类、枚举等。

内容的提问来源于stack exchange,提问作者S.Marius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:23:02