如何在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实现:
- 定义标记属性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)] public sealed class GenerateModulusAttribute : Attribute { public int Value { get; } public GenerateModulusAttribute(int value) => Value = value; }
- 编写源代码生成器(核心逻辑)
[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); } } } }
- 使用方式
// 标记后自动生成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
相关产品推荐
相关产品推荐

