C#中泛型类特定类型实现的最优方案探讨
针对你的文本对比场景,以下是几种性能优异且可读性良好的实现方式,替代你当前的方案:
1. 优化IEquatable约束的写法
首先明确:当你给泛型类加上IEquatable<TData>约束后,直接调用值类型的Equals方法不会发生装箱。JIT编译器会为每个具体的TData(如int、char)生成专属的代码,直接调用泛型版本的Equals,而非object.Equals。
因此你无需手动强制转换接口,代码可以简化为:
public class ComparisonContext<TData> where TData : struct, IEquatable<TData> { public TData[] DataA { get; } public TData[] DataB { get; } public ComparisonContext(TData[] dataA, TData[] dataB) { DataA = dataA; DataB = dataB; } // 直接调用Equals,无装箱 public bool AreEqual(int x, int y) { return DataA[x].Equals(DataB[y]); } }
这种方案最简单,可读性强,性能完全满足你的需求,因为int和char都默认实现了IEquatable<T>。
2. 利用JIT优化的类型分支实现专属对比
如果追求极致性能,可以直接使用值类型的==操作符(比Equals少一层方法调用),通过类型检查让JIT为每种TData生成专属代码:
public class ComparisonContext<TData> where TData : struct { public TData[] DataA { get; } public TData[] DataB { get; } public ComparisonContext(TData[] dataA, TData[] dataB) { DataA = dataA; DataB = dataB; } public bool AreEqual(int x, int y) { if (typeof(TData) == typeof(int)) { // JIT会为int类型的Context消除其他分支,直接执行此逻辑 return ((int[])(object)DataA)[x] == ((int[])(object)DataB)[y]; } else if (typeof(TData) == typeof(char)) { // JIT会为char类型的Context消除其他分支,直接执行此逻辑 return ((char[])(object)DataA)[x] == ((char[])(object)DataB)[y]; } else { return EqualityComparer<TData>.Default.Equals(DataA[x], DataB[y]); } } }
由于泛型类型在编译时是确定的,JIT会自动消除无关分支,实际运行时没有类型检查的开销,性能接近手写非泛型代码。
3. 比较器注入方案(扩展性最优)
通过抽象对比逻辑,为int和char实现专属比较器,注入到上下文类中,兼顾可读性和扩展性:
// 抽象比较器接口 public interface IValueComparer<T> where T : struct { bool Equals(T a, T b); } // int专属比较器 public class IntComparer : IValueComparer<int> { public bool Equals(int a, int b) => a == b; } // char专属比较器 public class CharComparer : IValueComparer<char> { public bool Equals(char a, char b) => a == b; } // 泛型上下文类 public class ComparisonContext<TData> where TData : struct { private readonly IValueComparer<TData> _comparer; public TData[] DataA { get; } public TData[] DataB { get; } public ComparisonContext(TData[] dataA, TData[] dataB, IValueComparer<TData> comparer) { DataA = dataA; DataB = dataB; _comparer = comparer; } public bool AreEqual(int x, int y) { return _comparer.Equals(DataA[x], DataB[y]); } } // 静态工厂简化创建 public static class ComparisonContextFactory { public static ComparisonContext<int> CreateForLines(int[] lineHashesA, int[] lineHashesB) { return new ComparisonContext<int>(lineHashesA, lineHashesB, new IntComparer()); } public static ComparisonContext<char> CreateForChars(char[] charsA, char[] charsB) { return new ComparisonContext<char>(charsA, charsB, new CharComparer()); } }
使用时直接调用工厂方法,无需关心比较器细节:
var lineContext = ComparisonContextFactory.CreateForLines(hashA, hashB); var charContext = ComparisonContextFactory.CreateForChars(charA, charB);
这种方案扩展性极强,后续新增对比类型时只需添加对应的比较器和工厂方法即可。
4. 极致性能:Unsafe内存比较
如果追求最高性能,可以使用Unsafe.Equals直接比较值类型的内存,无需方法调用开销。需要引用System.Runtime.CompilerServices.Unsafe NuGet包,并使用unmanaged约束(C# 7.3+支持):
using System.Runtime.CompilerServices; public class ComparisonContext<TData> where TData : unmanaged { public TData[] DataA { get; } public TData[] DataB { get; } public ComparisonContext(TData[] dataA, TData[] dataB) { DataA = dataA; DataB = dataB; } public bool AreEqual(int x, int y) { return Unsafe.Equals(ref DataA[x], ref DataB[y]); } }
int和char都是unmanaged类型,这种方式直接进行内存级别的比较,性能达到极致,但代码依赖Unsafe包,适合对性能要求极高的场景。
内容的提问来源于stack exchange,提问作者Spook

