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

如何在C#泛型类中模拟非类型模板参数实现编译时类型安全?

在C#中实现编译时类型安全的模数类型

C#原生不支持将整数直接作为泛型参数(类似C++的非类型模板参数),但可以通过用类型承载整数常量的方式,实现编译时类型安全的Modulo<T>,避免不同模数的实例混用。以下是两种可行方案:


方案1:静态抽象接口+极简结构体定义

这是最直接的原生实现,无需额外工具,仅需为每个常用模数定义一行代码的结构体:

// 定义约束接口,要求类型提供静态的模数常量
public interface IModulus
{
    static abstract int Value { get; }
}

// 核心模数类,通过泛型约束确保编译时类型安全
public readonly struct Modulo<T> where T : IModulus
{
    public int RawValue { get; }

    // 构造时自动处理取模和负数
    public Modulo(int value)
    {
        var mod = T.Value;
        RawValue = value % mod;
        if (RawValue < 0) RawValue += mod;
    }

    // 仅允许同模数的实例相加
    public Modulo<T> Add(Modulo<T> other)
    {
        return new Modulo<T>(RawValue + other.RawValue);
    }

    // 可扩展其他运算(如乘法、减法)
    public Modulo<T> Multiply(Modulo<T> other)
    {
        return new Modulo<T>(RawValue * other.RawValue);
    }
}

// 快速定义所需模数类型,每个仅一行代码
public readonly struct Mod3 : IModulus { public static int Value => 3; }
public readonly struct Mod5 : IModulus { public static int Value => 5; }
public readonly struct Mod7 : IModulus { public static int Value => 7; }

使用示例

var a = new Modulo<Mod3>(4); // 等价于 1 (mod 3)
var b = new Modulo<Mod3>(5); // 等价于 2 (mod 3)
var sum = a.Add(b); // sum.RawValue = 0 (mod 3)

// 编译错误!不同模数的实例无法相加
// var invalidSum = new Modulo<Mod3>(1).Add(new Modulo<Mod5>(2));

优缺点

  • 优点:完全原生、编译时类型安全、实现简单无依赖。
  • 缺点:每个模数需手动定义一个极简结构体(成本极低,仅一行代码)。

方案2:Source Generator自动生成模数类型

如果需要支持任意整数且不想手动编写结构体,可以用C#源代码生成器自动生成IModulus实现:

  1. 定义标记属性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
public sealed class GenerateModulusAttribute : Attribute
{
    public int Value { get; }
    public GenerateModulusAttribute(int value) => Value = value;
}
  1. 编写源代码生成器(核心逻辑)
[Generator]
public class ModulusGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context) {}

    public void Execute(GeneratorExecutionContext context)
    {
        // 扫描所有标记了GenerateModulusAttribute的类型
        foreach (var type in context.Compilation.Assembly.GetTypes())
        {
            var attrs = type.GetCustomAttributes<GenerateModulusAttribute>(false);
            foreach (var attr in attrs)
            {
                var modValue = attr.Value;
                var structName = $"Mod{modValue}";
                // 自动生成模数结构体代码
                var source = $$"""
                public readonly struct {{structName}} : IModulus
                {
                    public static int Value => {{modValue}};
                }
                """;
                context.AddSource($"{structName}.g.cs", source);
            }
        }
    }
}
  1. 使用方式
// 标记后自动生成Mod3、Mod5、Mod7结构体
[GenerateModulus(3)]
[GenerateModulus(5)]
[GenerateModulus(7)]
public static class ModulusDefinitions {}

// 直接使用自动生成的类型
var a = new Modulo<Mod3>(4);
var b = new Modulo<Mod5>(6);

优缺点

  • 优点:无需手动编写模数结构体,支持任意整数,编译时自动生成,类型安全。
  • 缺点:需要掌握源代码生成器的编写知识,有一定学习门槛。

优化:用C# 12类型别名简化调用

不管用哪种方案,都可以用C# 12的类型别名让代码更接近你想要的Modulo<3>写法:

using Mod3 = Modulo<Mod3>;
using Mod5 = Modulo<Mod5>;

// 使用时更直观
Mod3 a = new(4);
Mod3 b = new(5);
Mod3 sum = a.Add(b);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 18:33:17