在领域层结构中用Int64替代DateTime/TimeSpan是否合理?
问题描述
我在.NET Framework的领域项目中定义了如下简单结构体:
public struct Candlestick { public DateTime OpenTime; public DateTime CloseTime; public double Open; public double High; public double Low; public double Close; public double Volume; }
业务逻辑中存在大量对Candlestick结构体列表执行时间搜索的方法,例如:
public static int IndexOfFirst(this IReadOnlyList<Candlestick> data, DateTime from) { if (data == null) throw new ArgumentNullException(nameof(data)); int count = data.Count; if (count == 0) return -1; int fromIdx = 0; while (data[fromIdx].OpenTime < from) { if (++fromIdx == count) { return -1; } } return fromIdx; }
请问将Candlestick结构体中的时间变量改为long(Int64)类型,以提升逻辑层的操作性能是否合理?同时我计划在应用层(视图模型层的CandlestickVM)保留DateTime类型的时间变量。
为验证该想法,我在测试环境中进行了简单的性能测试:
DateTime a = new DateTime(2023, 01, 01); DateTime b = new DateTime(2023, 02, 01); TimeSpan incr = new TimeSpan(1); DateTime now = a; Stopwatch sw = Stopwatch.StartNew(); while (now < b) { now += incr; } sw.Stop(); Console.WriteLine($"It took {sw.Elapsed} to run the Date & Time test."); long incr2 = incr.Ticks; long now2 = a.Ticks; long bticks = b.Ticks; sw.Restart(); while (now2 < bticks) { now2 += incr2; } sw.Stop(); Console.WriteLine($"It took {sw.Elapsed} to run the long test.");
测试结果如下:
It took 00:00:04.8027716 to run the Date & Time test. It took 00:00:00.9261338 to run the long test.
回答
这个改造方案完全合理,核心原因和注意事项如下:
1. 性能提升的核心逻辑
DateTime在.NET中本质是包装了long类型的Ticks值,但每次对DateTime做比较、加减操作时,会额外触发结构体的内部校验(比如日期范围合法性检查),而直接用long做数值比较是原生CPU操作,无额外开销——你的测试结果已经直观体现了这种差距,在大量循环的搜索场景中,这种性能提升会被进一步放大。
2. 分层设计的合理性
你计划的「领域层用long、应用层保留DateTime」完全符合关注点分离原则:
- 领域层聚焦高效业务计算,不需要处理日期格式化、UI展示等逻辑,用
long能最大化性能; - 应用层(视图模型)需要和UI交互,
DateTime更便于绑定、格式化和用户理解,两者间的转换(new DateTime(ticks)或dateTime.Ticks)开销可以忽略不计。
3. 需注意的细节
- 精度一致性:转换时统一使用
Ticks作为单位,避免混用毫秒、秒等其他单位导致时间偏差; - 代码可读性:给
long类型的时间字段加明确命名(比如OpenTimeTicks),避免后续维护时混淆数值含义; - 边界校验:如果业务涉及超出
DateTime有效范围的时间,long能提供更大取值空间;若始终在DateTime范围内,转换时需校验避免生成无效实例。
4. 进一步优化建议
当前的IndexOfFirst是线性搜索,若数据量较大,建议改用二分查找——无论用DateTime还是long,二分查找时间复杂度为O(logN),远优于线性搜索的O(N),结合long的高性能比较,整体效率会有质的提升:
public static int IndexOfFirst(this IReadOnlyList<Candlestick> data, long fromTicks) { if (data == null) throw new ArgumentNullException(nameof(data)); int left = 0; int right = data.Count - 1; int result = -1; while (left <= right) { int mid = left + (right - left) / 2; if (data[mid].OpenTimeTicks >= fromTicks) { result = mid; right = mid - 1; } else { left = mid + 1; } } return result; }
内容的提问来源于stack exchange,提问作者Timur Rozhok
相关产品推荐
相关产品推荐

