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

如何实现基于数值的类型安全性?(类似泛型的类型依赖机制)

在C#中实现基于特定数值的强类型Level类(避免手动定义LevelXXX类型)

要实现你需要的「不同数值的Level类型互不兼容,且无需手动定义每个数值对应的单独类型」的功能,核心是利用C#的泛型类型系统区分不同数值标识,同时通过源代码生成器自动生成对应的标记类型,避免手动编写重复代码。

实现步骤

1. 定义标记接口与泛型Level类

首先创建一个标记接口,用于约束泛型参数;再定义泛型Level<T>类,确保不同T对应的Level类型完全独立:

// 标记接口,用于标识等级数值类型
public interface ILevelMarker
{
    // 静态抽象成员,获取对应的等级数值(C# 11+支持)
    static abstract int LevelValue { get; }
}

// 泛型Level类,泛型参数必须是实现ILevelMarker的结构体
public class Level<T> where T : struct, ILevelMarker
{
    // 可添加业务属性,比如玩家名称、经验值等
    public string PlayerName { get; set; }

    // 可以通过静态成员获取当前Level对应的数值
    public int CurrentLevelValue => T.LevelValue;
}

2. 用源代码生成器自动生成标记结构体

为了避免手动编写Level1024、Level1000这类结构体,我们可以用源代码生成器自动生成对应数值的标记类型:

第一步:定义生成特性

创建一个特性,用于标记需要生成的等级数值:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
public class GenerateLevelMarkerAttribute : Attribute
{
    public int Value { get; }
    public GenerateLevelMarkerAttribute(int value)
    {
        Value = value;
    }
}
第二步:编写源代码生成器

创建一个Source Generator项目,实现自动生成标记结构体的逻辑:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Immutable;

[Generator]
public class LevelMarkerGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 筛选带有GenerateLevelMarkerAttribute的类型
        var provider = context.SyntaxProvider
            .CreateSyntaxProvider(
                (node, _) => node is ClassDeclarationSyntax || node is StructDeclarationSyntax,
                (ctx, _) => GetTargetType(ctx)
            )
            .Where(t => t != null);

        // 合并结果并生成代码
        context.RegisterSourceOutput(provider.Collect(), (spc, types) =>
        {
            foreach (var type in types.Where(t => t != null))
            {
                GenerateLevelMarkerCode(spc, type);
            }
        });
    }

    private static INamedTypeSymbol? GetTargetType(GeneratorSyntaxContext context)
    {
        var symbol = context.SemanticModel.GetDeclaredSymbol(context.Node);
        if (symbol == null) return null;

        // 检查是否带有GenerateLevelMarkerAttribute
        var attr = symbol.GetAttributes()
            .FirstOrDefault(a => a.AttributeClass?.Name == nameof(GenerateLevelMarkerAttribute));
        return attr != null ? symbol : null;
    }

    private static void GenerateLevelMarkerCode(SourceProductionContext context, INamedTypeSymbol type)
    {
        var attributes = type.GetAttributes()
            .Where(a => a.AttributeClass?.Name == nameof(GenerateLevelMarkerAttribute));

        foreach (var attr in attributes)
        {
            if (attr.ConstructorArguments.First().Value is int levelValue)
            {
                var structName = $"Level{levelValue}";
                // 生成代码
                var code = $$"""
                    using System;

                    namespace YourNamespace;

                    public readonly struct {{structName}} : ILevelMarker
                    {
                        public static int LevelValue => {{levelValue}};
                    }
                    """;
                context.AddSource($"{structName}.g.cs", code);
            }
        }
    }
}

3. 使用方式

在项目中添加一个类,标记需要生成的等级数值:

[GenerateLevelMarker(1024)]
[GenerateLevelMarker(1000)]
public static class LevelDefinitions
{
}

之后就可以直接使用泛型Level类,不同数值的类型会被编译器视为完全不同的类型,赋值时会报错:

Level<Level1024> topPlayerOf1K;
Level<Level1000> Abe = new Level<Level1000> { PlayerName = "Abe" };
topPlayerOf1K = Abe; // 编译错误:无法将Level<Level1000>隐式转换为Level<Level1024>

替代方案(无源代码生成器)

如果暂时不想用源代码生成器,可以手动编写少量标记结构体,这种方式适合数值较少的场景:

public readonly struct Level1024 : ILevelMarker { public static int LevelValue => 1024; }
public readonly struct Level1000 : ILevelMarker { public static int LevelValue => 1000; }

使用方式和上面一致,同样能实现类型不兼容的效果。


内容的提问来源于stack exchange,提问作者ispiro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 19:05:24