如何在泛型中实现无符号与有符号类型的互相推导?
解决泛型数值类型的有符号/无符号类型映射问题
你遇到的核心问题是C#泛型系统本身没有内置机制直接关联有符号与无符号数值类型的对应关系,不过我们可以通过几种实用方案来解决这个问题,下面给你具体的实现思路和代码:
方案1:显式类型匹配(性能最优)
因为.NET支持的数值类型是有限的,我们可以直接针对每个有符号/无符号对做显式判断和转换,这种方式不需要反射,性能最好,维护成本也很低。
完善后的Number<T>类代码如下:
public static class Number<T> { // 判断当前T是否为有符号类型 private static readonly bool IsSigned = typeof(T) switch { Type t when t == typeof(sbyte) || t == typeof(short) || t == typeof(int) || t == typeof(long) => true, Type t when t == typeof(byte) || t == typeof(ushort) || t == typeof(uint) || t == typeof(ulong) => false, _ => throw new NotSupportedException($"类型 {typeof(T)} 不是受支持的数值类型。") }; private static readonly bool IsUnsigned = !IsSigned; // 假设你已实现的泛型转换方法 private static TU SignedToUnsigned<TS, TU>(TS input) where TS : struct, IComparable, IConvertible where TU : struct, IComparable, IConvertible { // 这里可以替换成你自己的转换逻辑,比如处理溢出等 return (TU)Convert.ChangeType(input, typeof(TU)); } private static TS UnsignedToSigned<TU, TS>(TU input) where TU : struct, IComparable, IConvertible where TS : struct, IComparable, IConvertible { // 这里可以替换成你自己的转换逻辑 return (TS)Convert.ChangeType(input, typeof(TS)); } public static object ToUnsigned(T input) { if (IsUnsigned) return input; // 根据T的类型匹配对应的无符号类型并转换 return typeof(T) switch { Type t when t == typeof(sbyte) => SignedToUnsigned<sbyte, byte>((sbyte)(object)input), Type t when t == typeof(short) => SignedToUnsigned<short, ushort>((short)(object)input), Type t when t == typeof(int) => SignedToUnsigned<int, uint>((int)(object)input), Type t when t == typeof(long) => SignedToUnsigned<long, ulong>((long)(object)input), _ => throw new NotSupportedException($"无法将有符号类型 {typeof(T)} 转换为无符号类型。") }; } public static object ToSigned(T input) { if (IsSigned) return input; // 根据T的类型匹配对应的有符号类型并转换 return typeof(T) switch { Type t when t == typeof(byte) => UnsignedToSigned<byte, sbyte>((byte)(object)input), Type t when t == typeof(ushort) => UnsignedToSigned<ushort, short>((ushort)(object)input), Type t when t == typeof(uint) => UnsignedToSigned<uint, int>((uint)(object)input), Type t when t == typeof(ulong) => UnsignedToSigned<ulong, long>((ulong)(object)input), _ => throw new NotSupportedException($"无法将无符号类型 {typeof(T)} 转换为有符号类型。") }; } }
方案2:类型映射字典+反射(扩展性更好)
如果你不想手动枚举每个类型,可以创建一个类型映射字典,再通过反射调用你的泛型转换方法,这种方式扩展更灵活:
public static class Number<T> { // 定义有符号→无符号的类型映射 private static readonly Dictionary<Type, Type> _signedToUnsignedMap = new Dictionary<Type, Type> { { typeof(sbyte), typeof(byte) }, { typeof(short), typeof(ushort) }, { typeof(int), typeof(uint) }, { typeof(long), typeof(ulong) } }; // 定义无符号→有符号的类型映射 private static readonly Dictionary<Type, Type> _unsignedToSignedMap = new Dictionary<Type, Type> { { typeof(byte), typeof(sbyte) }, { typeof(ushort), typeof(short) }, { typeof(uint), typeof(int) }, { typeof(ulong), typeof(long) } }; private static readonly bool IsSigned = _signedToUnsignedMap.ContainsKey(typeof(T)); private static readonly bool IsUnsigned = _unsignedToSignedMap.ContainsKey(typeof(T)); // 假设你已实现的泛型转换方法 private static TU SignedToUnsigned<TS, TU>(TS input) where TS : struct, IComparable, IConvertible where TU : struct, IComparable, IConvertible { return (TU)Convert.ChangeType(input, typeof(TU)); } private static TS UnsignedToSigned<TU, TS>(TU input) where TU : struct, IComparable, IConvertible where TS : struct, IComparable, IConvertible { return (TS)Convert.ChangeType(input, typeof(TS)); } public static object ToUnsigned(T input) { if (IsUnsigned) return input; if (!_signedToUnsignedMap.TryGetValue(typeof(T), out var unsignedType)) throw new NotSupportedException($"类型 {typeof(T)} 没有对应的无符号类型。"); // 通过反射调用泛型转换方法 var method = typeof(Number<T>).GetMethod(nameof(SignedToUnsigned), BindingFlags.NonPublic | BindingFlags.Static)!; var genericMethod = method.MakeGenericMethod(typeof(T), unsignedType); return genericMethod.Invoke(null, new object[] { input })!; } public static object ToSigned(T input) { if (IsSigned) return input; if (!_unsignedToSignedMap.TryGetValue(typeof(T), out var signedType)) throw new NotSupportedException($"类型 {typeof(T)} 没有对应的有符号类型。"); // 通过反射调用泛型转换方法 var method = typeof(Number<T>).GetMethod(nameof(UnsignedToSigned), BindingFlags.NonPublic | BindingFlags.Static)!; var genericMethod = method.MakeGenericMethod(typeof(T), signedType); return genericMethod.Invoke(null, new object[] { input })!; } }
额外注意事项
- 转换时要处理溢出问题:比如将
ulong转换为long时,如果数值超过long.MaxValue会抛出异常,你可以在转换方法中添加checked或unchecked块来控制溢出行为。 - 确保只支持数值类型:在类型判断逻辑中要排除非数值类型,避免出现意外错误。
内容的提问来源于stack exchange,提问作者user6038265
相关产品推荐
相关产品推荐

