C#基础11-常用类
1、String
(1)基础特性
- 不可变性
- C# 字符串(
string)是引用类型,但内容不可修改。
- 每次操作(如拼接、替换)都会创建新对象,旧对象由 GC 回收。频繁操作可能影响性能。
- 类似字符数组
- 可通过索引访问单个字符(如
str[0]),但不能直接修改(如 str[0] = 'a' 会报错)。
- 需转换为
char[] 修改后再转回字符串:
char[] chars = str.ToCharArray();
chars[1] = '为';
str = new string(chars); // 重新生成字符串
(2)常用操作
- 大小写转换:忽略用户输入大小写(如
Console.ReadLine().ToLower())
string lower = str.ToLower(); // 转为小写
string upper = str.ToUpper(); // 转为大写
string trimmed = str.Trim(); // 移除首尾空格
string customTrim = str.Trim('a', 'b'); // 移除首尾指定字符(如 'a'/'b')
// 分割
string[] words = str.Split(new char[] { ' ', ',', '?' }, StringSplitOptions.RemoveEmptyEntries);
// 合并
string joined = string.Join("-", new string[] { "A", "B", "C" }); // 输出 "A-B-C"
string sub1 = str.Substring(3); // 从索引3截取到末尾
string sub2 = str.Substring(3, 2); // 从索引3截取2个字符
bool contains = str.Contains("abc"); // 是否包含子串
int index = str.IndexOf("abc"); // 首次出现位置(不存在返回-1)
bool starts = str.StartsWith("He"); // 是否以指定字符串开头
byte[] utf8Bytes = Encoding.UTF8.GetBytes(str); // 字符串 → UTF-8字节
string fromGBK = Encoding.GetEncoding("GBK").GetString(bytes); // 字节 → 字符串
(3)性能优化
- 避免频繁拼接:循环内拼接字符串时,优先使用
StringBuilder,效率比+= 提升数十倍
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.Append("text");
}
string result = sb.ToString(); // 最终生成字符串
- 高效字符串比较 :使用
StringComparison.Ordinal 或 OrdinalIgnoreCase 进行字节级比较,避免文化差异影响,性能提升显著:
bool equal = string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase);
(4)格式化输出
String.Format 与占位符:{index[,alignment][:formatString]}
string.Format("Name: {0,-10} Salary: {1,6:C}", "Bill", 123456);
// 输出:Name: Bill Salary: ¥123,456.00
- `index`:参数索引(从0开始)
- `alignment`:对齐方式(正数右对齐,负数左对齐)
- `formatString`:格式代码
string name = "Polly";
decimal salary = 7890.5m;
Console.WriteLine($"{name,-10} {salary,6:C}");
// 输出:Polly ¥7,890.50
| 标识符 |
说明 |
示例(输入123456.42) |
输出 |
C/c |
货币格式 |
{0:C2} |
¥123,456.42 |
D/d |
十进制整数 |
{0:D8} |
00123456 |
F/f |
定点小数 |
{0:F3} |
123456.420 |
N/n |
千位分隔数字 |
{0:N0} |
123,456 |
P/p |
百分比 |
{0:P1} (输入0.42) |
42.0% |
X/x |
十六进制 |
{0:X} (输入255) |
FF |
E/e |
科学计数法 |
{0:E2} |
1.23E+005 |
| 符号 |
作用 |
示例(输入1234.56) |
输出 |
0 |
零占位符(补位) |
{0:000000.000} |
001234.560 |
# |
数字占位符(去空) |
{0:####.##} |
1234.56 |
. |
小数点 |
{0:0.000} |
1234.560 |
, |
千位分隔符/缩放倍数 |
{0:0,.} {0:0,,} |
1 (缩放千/百万倍) |
% |
百分比转换 |
{0:0%} (输入0.42) |
42% |
; |
分段格式化(正/负/零) |
{0:正数:#;负数:(#);零} |
正数:1234 |
Console.WriteLine($"{123456.42:#,##0.00;(#,##0.00);zero}"); // 123,456.42
Console.WriteLine($"{-123456.42:#,##0.00;(#,##0.00);zero}"); // (123,456.42)
- 区域性设置:使用
CultureInfo 适配本地化格式
double value = 1234.56;
string enUS = string.Format(CultureInfo.GetCultureInfo("en-US"), "{0:C}", value); // $1,234.56
string zhCN = string.Format(CultureInfo.GetCultureInfo("zh-CN"), "{0:C}", value); // ¥1,234.56
2、StringBuilder
(1)基础用法
// 初始化字符串 + 设置初始容量
StringBuilder sb = new StringBuilder("Hello", 20); //
Console.WriteLine($"Capacity: {sb.Capacity}"); // 输出 20
sb.Append(" World!"); // "Hello World!"
- `AppendFormat()`:格式化追加
int price = 25;
sb.AppendFormat(" Price: {0:C}", price); // "Hello World! Price: $25.00"
sb.Insert(6, "C# "); // "Hello C# World!"
- `Remove()`:删除子串
sb.Remove(5, 4); // 删除索引5开始的4个字符 → "Hello World!"
sb.Replace("World", "Universe"); // "Hello Universe!"
- `Clear()`:清空内容
sb.Clear(); // 长度归零
(2)性能优化
- 预分配容量
- 避免频繁扩容:初始化时预估最大长度(如日志拼接),减少内存复制开销。
- 避免链式操作
- 错误示例:
sb.Append("A").Append("B").Append("C");
- 改进:单次
Append()合并内容:
sb.Append("A").Append("B"); // 少量操作可接受
- Unity游戏开发场景
- 在循环中拼接字符串时,
StringBuilder比String减少90%的GC(垃圾回收)压力:
StringBuilder result = new StringBuilder();
for (int i = 0; i < 1000; i++) {
result.Append(i); // 无临时字符串产生
}
(3)与String的差异
| 特性 |
String |
StringBuilder |
| 可变性 |
不可变(修改生成新对象) |
可变(原地修改字符数组) |
| 内存开销 |
高(频繁修改时) |
低(预分配缓冲池) |
| 线程安全 |
是 |
❌ 非线程安全(需手动同步) |
| 适用场景 |
单次操作、常量字符串 |
循环拼接、动态构建大文本 |
(4)高级方法
char[] buffer = new char[10];
sb.CopyTo(0, buffer, 0, 5); // 复制前5字符到数组
sb.EnsureCapacity(50); // 确保容量≥50
if (sb.Length == 0) { /* 空对象 */ } // 比ToString()更高效
(5)实战示例
using System.Text;
StringBuilder log = new StringBuilder("Start: ", 100);
// 批量追加
log.AppendLine("Operation 1 completed.")
.AppendFormat("Time: {0:HH:mm}", DateTime.Now);
// 替换关键词
log.Replace("completed", "SUCCESS");
// 输出结果
Console.WriteLine(log.ToString());
输出:
Start: Operation 1 SUCCESS.
Time: 14:30
3、DateTime
(1)基础操作
DateTime now = DateTime.Now; // 当前本地时间
DateTime utcNow = DateTime.UtcNow; // 当前 UTC 时间
DateTime today = DateTime.Today; // 当天日期(时间部分为 00:00:00)
- 属性示例
now.Year(年份)
now.Month(月份)
now.Day(日)
now.Hour(小时)
now.Minute(分钟)
now.Second(秒)
- 构造指定日期
DateTime customDate = new DateTime(2025, 9, 3); // 2025年9月3日
DateTime withTime = new DateTime(2025, 9, 3, 14, 30, 0); // 含具体时间
(2)格式化输出
string shortDate = now.ToString("d"); // "2025/9/3"(短日期)
string longDate = now.ToString("D"); // "2025年9月3日"
string iso8601 = now.ToString("s"); // "2025-09-03T17:15:55"(ISO 标准)
string customFormat = now.ToString("yyyy-MM-dd HH:mm:ss"); // "2025-09-03 17:15:55"
string monthName = now.ToString("MMM", new CultureInfo("en-US")); // "SEP"(英文月份缩写)
string dateOnly = now.ToShortDateString(); // 短日期字符串(如 "2025/9/3")
string timeOnly = now.ToShortTimeString(); // 短时间字符串(如 "17:15")
️(3)比较与判断
int result = DateTime.Compare(date1, date2);
// result < 0:date1 早于 date2;result > 0:date1 晚于 date2
bool isLater = date1 > date2; // 判断先后
bool isSameDay = date1.Date == date2.Date; // 是否同一天
DayOfWeek day = now.DayOfWeek; // 返回枚举(如 DayOfWeek.Wednesday)
bool isLeapYear = DateTime.IsLeapYear(now.Year); // 是否为闰年
(4)运算与时间差
DateTime tomorrow = now.AddDays(1); // 加1天
DateTime lastMonth = now.AddMonths(-1); // 减1个月
DateTime nextHour = now.AddHours(1); // 加1小时
TimeSpan diff = date2 - date1;
Console.WriteLine($"相差:{diff.Days}天,{diff.Hours}小时");
- 精确单位:
diff.TotalDays(总天数)、diff.TotalMinutes(总分钟数)
(5)实用场景
DateTime lastDay = new DateTime(now.Year, now.Month, 1).AddMonths(1).AddDays(-1);
// 或使用内置方法:
int lastDayNum = DateTime.DaysInMonth(now.Year, now.Month); // 当月总天数
int workingDays = 0;
for (DateTime d = startDate; d <= endDate; d = d.AddDays(1))
{
if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday)
workingDays++;
}
(6)注意事项
- 时区处理:涉及跨时区应用时,优先使用
DateTime.UtcNow 协调时间,避免本地时区混淆。
- 性能优化:频繁操作日期时(如循环计算),避免重复调用
DateTime.Now(每次调用需系统交互),可缓存初始值。
4、TimeSpan
(1)创建实例
- 构造函数 :参数顺序
(days, hours, minutes, seconds, milliseconds)。
TimeSpan ts1 = new TimeSpan(10, 30, 0); // 10小时30分钟
TimeSpan ts2 = new TimeSpan(2, 14, 18, 35); // 2天14小时18分35秒
TimeSpan ts3 = new TimeSpan(0, 0, 0, 0, 500); // 500毫秒
TimeSpan.FromDays(3.5); // 3天12小时
TimeSpan.FromMinutes(90); // 1小时30分钟
TimeSpan.FromMilliseconds(1500); // 1.5秒
(2)算时间差
DateTime start = new DateTime(2025, 1, 1, 8, 0, 0);
DateTime end = new DateTime(2025, 1, 1, 17, 30, 45);
TimeSpan duration = end - start; // 或 end.Subtract(start);
Console.WriteLine(duration); // 输出: 09:30:45
- 结果可通过属性获取部分值(如
duration.Hours 返回 9)或总量(如 duration.TotalHours 返回 9.5125)。
(3)关键属性
| 属性 |
说明 |
示例值 (duration) |
Days |
间隔的天数部分(不包含小时转换) |
0 |
Hours |
小时部分(0-23) |
9 |
TotalHours |
总小时数(含小数) |
9.5125 |
TotalMinutes |
总分钟数 |
570.75 |
Milliseconds |
毫秒部分(0-999) |
0 |
Ticks |
100纳秒为单位的间隔(1秒=10^7) |
342450000000 |
(4)格式转换
- 字符串转 TimeSpan:
- 支持格式:
"6:15:30"、"1.12:24:02"(1天12小时)。
TimeSpan ts = TimeSpan.Parse("06:15:30"); // 格式: [d.]hh:mm:ss[.fffffff]
bool success = TimeSpan.TryParse("6.15:30", out ts); // 安全解析(推荐)
ts.ToString(@"dd\.hh\:mm\:ss"); // 输出 "01.12:24:02"
$"{ts:%d}天 {ts:%h}小时"; // 输出 "1天 12小时"
(5)常见问题
- 精度问题:
DateTime 最小单位是毫秒,TimeSpan 最小单位是 Tick(100纳秒),高精度计算需用 Ticks。
- 负数间隔:若
start > end,TimeSpan 属性值为负,可用 Duration() 取绝对值。
5、Stopwatch
(1)核心功能
Stopwatch stopwatch = new Stopwatch(); // 创建实例
stopwatch.Start(); // 启动计时
// 执行待测代码(如数据库查询、算法等)
stopwatch.Stop(); // 停止计时
TimeSpan elapsed = stopwatch.Elapsed; // 获取时间间隔
Console.WriteLine($"耗时:{elapsed.TotalMilliseconds}ms");
// 一行代码完成计时
TimeSpan elapsed = Stopwatch.StartNew(() => {
// 待测代码
}).Elapsed;
(2)注意事项
for (int i = 0; i < 10; i++) {
Stopwatch sw = new Stopwatch(); // 可能因种子相同导致时间值重复
sw.Start();
// ...
}
- 正确做法(复用静态实例):
static Stopwatch sw = new Stopwatch(); // 类级静态对象
for (int i = 0; i < 10; i++) {
sw.Restart(); // 重置并启动
// ...
sw.Stop();
}
| 场景 |
方案 |
| .NET 6+ |
直接使用线程安全的 Stopwatch.StartNew() |
| 旧版本.NET |
为每个线程分配独立实例:new ThreadLocal<Random>(() => new Stopwatch()) |
- 精度与局限性
- 最高精度:依赖硬件支持,通常为 100纳秒级(通过
Stopwatch.IsHighResolution 检测)。
- 不适用场景:加密、安全相关操作(需改用
RNGCryptoServiceProvider)。
(3)高级技巧
Stopwatch sw = Stopwatch.StartNew();
MethodA();
sw.Stop();
TimeSpan timeA = sw.Elapsed;
sw.Restart(); // 重置计时器
MethodB();
sw.Stop();
TimeSpan timeB = sw.Elapsed;
var timings = new List<long>();
for (int i = 0; i < 100; i++) {
sw.Restart();
// 执行代码
sw.Stop();
timings.Add(sw.ElapsedTicks); // 记录每次耗时(高精度)
}
double avgTicks = timings.Average();
using (SqlConnection conn = new SqlConnection(connStr)) {
conn.Open();
Stopwatch sw = Stopwatch.StartNew();
// 执行查询
var data = conn.Query("SELECT * FROM LargeTable");
sw.Stop();
Log($"查询耗时:{sw.ElapsedMilliseconds}ms");
}
(4)场景优化
| 场景 |
推荐实践 |
| 短时操作测量 |
多次运行取平均值(避免单次误差) |
| Web API响应监控 |
在中间件中集成Stopwatch,记录接口耗时 |
| 算法效率对比 |
结合GC.Collect()和GC.WaitForPendingFinalizers()排除GC干扰 |
| 高频调用代码分析 |
使用ElapsedTicks替代毫秒计数(更高精度) |
6、Math
(1)数学运算
- 取绝对值
Math.Abs(-5.3) → 5.3(支持整数、浮点数)
- 幂与根号
- 幂运算:
Math.Pow(2, 3) → 8(计算2的3次方)
- 平方根:
Math.Sqrt(9) → 3
- 指数与对数
- 自然指数:
Math.Exp(2) ≈ 7.389(e²)
- 常用对数:
Math.Log10(100) → 2(log₁₀100)
(2)取整与舍入
| 方法 |
说明 |
示例 |
结果 |
Math.Ceiling |
向上取整 |
Math.Ceiling(2.1) |
3 |
Math.Floor |
向下取整 |
Math.Floor(2.9) |
2 |
Math.Round (默认) |
银行家算法(四舍六入五取偶) |
Math.Round(2.5) |
2 |
Math.Round (指定模式) |
强制四舍五入 |
Math.Round(2.5, MidpointRounding.AwayFromZero) |
3 |
Math.Round(Convert.ToDecimal(526.925), 2, MidpointRounding.AwayFromZero); // 返回526.93
(3)三角函数
Math.Sin(Math.PI/6) → 0.5(参数为弧度)
Math.Atan2(y, x):计算点 (x,y) 的反正切角(自动处理象限)
double angle = Math.Atan2(7, 7) * 180 / Math.PI; // 输出45°
(4)高级工具库
- 功能:矩阵运算、线性代数、微积分等科学计算。
- 安装:
Install-Package MathNet.Numerics
using MathNet.Numerics.LinearAlgebra;
var matrix = DenseMatrix.OfArray(new double[,] {{1, 2}, {3, 4}});
var subMatrix = matrix.SubMatrix(0, 2, 0, 1); // 提取前两行、第一列
(5)关键常量
Math.PI:圆周率π ≈ 3.14159
Math.E:自然对数底e ≈ 2.71828
(6)最佳实践
- 精度问题:财务计算优先用
decimal,避免 double 浮点误差。
- 矩阵运算:简单矩阵用原生数组,复杂计算(如求逆、特征值)用 Math.NET(效率提升10倍+)。
7、Random
(1)核心功能
Random rand = new Random(); // 推荐在类级别声明为静态对象
- 指定种子:相同种子生成相同随机序列(适用于测试)
Random fixedRand = new Random(123); // 种子值固定
| 方法 |
作用 |
示例 |
Next() |
生成0~2,147,483,647的整数 |
rand.Next() → 198273 |
Next(int max) |
生成0~max-1的整数 |
rand.Next(100) → 0~99 |
Next(int min, int max) |
生成min~max-1的整数 |
rand.Next(10, 20) → 10~19 |
NextDouble() |
生成0.0~1.0的双精度浮点数 |
rand.NextDouble() → 0.784 |
NextBytes(byte[] buffer) |
填充随机字节数组 |
byte[] buf = new byte[8]; rand.NextBytes(buf); |
(2)注意事项
for (int i = 0; i < 5; i++)
{
Random r = new Random(); // 可能生成相同随机数(间隔<15ms时)
Console.WriteLine(r.Next());
}
- 正确做法:复用同一实例!
static Random rand = new Random(); // 静态对象保证单例
for (int i = 0; i < 5; i++)
{
Console.WriteLine(rand.Next(100)); // 输出不同随机数
}
- 多线程安全方案
- .NET 6+:使用线程安全的
Random.Shared
int num = Random.Shared.Next(1, 100); // 无需手动管理实例
- 旧版本:为每个线程创建独立实例
private static readonly ThreadLocal<Random> threadRand =
new ThreadLocal<Random>(() => new Random(Guid.NewGuid().GetHashCode()));
- 随机性局限性
Random 生成的是伪随机数,不适合加密等高安全场景(需改用 RNGCryptoServiceProvider)
- 浮点数精度:
NextDouble() 范围是 [0.0, 1.0),不含1.0
(3)进阶技巧
// 生成[-100, 100)的整数
int num = rand.Next(-100, 100);
// 生成[0.5, 5.5)的浮点数
double value = rand.NextDouble() * 5.0 + 0.5;
// 随机字符串(a-z, 0-9)
string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
string randomStr = new string(Enumerable.Repeat(chars, 8)
.Select(s => s[rand.Next(s.Length)]).ToArray()); // 如 "a3f8bz2r"
(4)最佳实践
| 场景 |
推荐方案 |
原因 |
| 单线程应用 |
静态 Random 实例 |
避免重复种子问题 |
| 多线程应用 |
Random.Shared(.NET 6+)或 ThreadLocal<Random> |
线程安全且高效 |
| 加密/安全场景 |
System.Security.Cryptography.RandomNumberGenerator |
真随机性要求 |
| 测试用例 |
固定种子的 Random |
结果可复现 |
8、Guid
(1)核心功能
Guid guid = Guid.NewGuid(); // 默认生成V4格式(128位随机值)
Console.WriteLine(guid); // 输出:9af7f46a-ea52-4aa3-b8c3-9fd484c2af12
- 时间序生成(V7,.NET 9+)
Guid guidV7 = Guid.CreateVersion7(); // 基于时间戳+随机数,适合数据库主键
Console.WriteLine(guidV7); // 输出:01917bbe-d973-7beb-a813-106fcb4eff98
| 格式符 |
输出示例 |
适用场景 |
D (默认) |
9af7f46a-ea52-4aa3-b8c3-... |
日志、显示 |
N |
e0a953c3ee6040eaa9fae2b6... |
紧凑存储(26字符) |
B |
{734fd453-a4f8-4c5d-...} |
序列化 |
X |
{0x3fa412e3,0x8356,...} |
底层调试 |
string guidStr = guid.ToString("N"); // 无连字符,节省28%存储空间
Guid parsedGuid = Guid.Parse("9af7f46a-ea52-4aa3-b8c3-9fd484c2af12");
// 安全解析(避免异常)
if (Guid.TryParse("invalid-guid", out Guid safeGuid)) { ... }
- 字节数组转换
byte[] bytes = guid.ToByteArray();
Guid fromBytes = new Guid(bytes); // 从字节数组还原
(2)注意事项
| 版本 |
特点 |
推荐场景 |
| V4 |
完全随机,全局唯一 |
通用标识、临时令牌 |
| V7 |
时间戳前缀,局部有序 |
数据库主键(减少索引碎片) |
| V1/V2 |
含MAC地址,隐私风险 |
已弃用 |
// ❌ 错误:每次循环新建实例可能导致重复(间隔<15ms时)
for (int i=0; i<1000; i++)
{
var g = Guid.NewGuid();
}
// ✅ 正确:复用单例
static readonly GuidGenerator = new Random();
Guid[] guids = Enumerable.Repeat(0, 1000).Select(_ => Guid.NewGuid()).ToArray();
- 存储优化
- 数据库字段 → 使用
uniqueidentifier 类型(SQL Server)
- 频繁传输 → 采用
N 格式字符串(26字符)
- 多线程安全
- .NET 6+:无需额外处理,
Guid.NewGuid() 内部线程安全
- 旧版本:通过
ThreadLocal 隔离实例
private static ThreadLocal<Random> _threadRand =
new ThreadLocal<Random>(() => new Random(Guid.NewGuid().GetHashCode()));
(3)高级技巧
public class Order
{
public Guid Id { get; } = Guid.CreateVersion7(); // .NET 9+
public string Product { get; set; }
}
string tempFile = $"{Guid.NewGuid():N}.tmp"; // 生成唯一文件名
Directory.CreateDirectory($"uploads/{Guid.NewGuid()}");
using System.Security.Cryptography;
byte[] cryptoBytes = new byte[16];
RandomNumberGenerator.Fill(cryptoBytes); // 真随机数
Guid secureGuid = new Guid(cryptoBytes);
(4)最佳实践
| 场景 |
推荐方案 |
原因 |
| 数据库主键 |
Guid.CreateVersion7() (NET9+) |
时序性减少索引碎片 |
| 高频生成标识符 |
静态单例 Guid.NewGuid() |
避免实例化开销 |
| 网络传输/存储 |
ToString("N") |
节省空间(26字符 vs 36字符) |
| 加密安全需求 |
RandomNumberGenerator + Guid构造 |
真随机性保障 |
| 历史代码兼容 |
Guid.Parse() + 格式校验 |
防止无效输入导致崩溃 |
9、Stopwatch
(1)核心功能
Stopwatch stopwatch = new Stopwatch(); // 创建实例
stopwatch.Start(); // 启动计时
// 执行待测代码(如数据库查询、算法等)
stopwatch.Stop(); // 停止计时
TimeSpan elapsed = stopwatch.Elapsed; // 获取时间间隔
Console.WriteLine($"耗时:{elapsed.TotalMilliseconds}ms");
// 一行代码完成计时
TimeSpan elapsed = Stopwatch.StartNew(() => {
// 待测代码
}).Elapsed;
(2)注意事项
for (int i = 0; i < 10; i++) {
Stopwatch sw = new Stopwatch(); // 可能因种子相同导致时间值重复
sw.Start();
// ...
}
- 正确做法(复用静态实例):
static Stopwatch sw = new Stopwatch(); // 类级静态对象
for (int i = 0; i < 10; i++) {
sw.Restart(); // 重置并启动
// ...
sw.Stop();
}
| 场景 |
方案 |
| .NET 6+ |
直接使用线程安全的 Stopwatch.StartNew() |
| 旧版本.NET |
为每个线程分配独立实例:new ThreadLocal<Random>(() => new Stopwatch()) |
- 精度与局限性
- 最高精度:依赖硬件支持,通常为 100纳秒级(通过
Stopwatch.IsHighResolution 检测)。
- 不适用场景:加密、安全相关操作(需改用
RNGCryptoServiceProvider)。
(3)高级技巧
Stopwatch sw = Stopwatch.StartNew();
MethodA();
sw.Stop();
TimeSpan timeA = sw.Elapsed;
sw.Restart(); // 重置计时器
MethodB();
sw.Stop();
TimeSpan timeB = sw.Elapsed;
var timings = new List<long>();
for (int i = 0; i < 100; i++) {
sw.Restart();
// 执行代码
sw.Stop();
timings.Add(sw.ElapsedTicks); // 记录每次耗时(高精度)
}
double avgTicks = timings.Average();
using (SqlConnection conn = new SqlConnection(connStr)) {
conn.Open();
Stopwatch sw = Stopwatch.StartNew();
// 执行查询
var data = conn.Query("SELECT * FROM LargeTable");
sw.Stop();
Log($"查询耗时:{sw.ElapsedMilliseconds}ms");
}
(4)场景优化
| 场景 |
推荐实践 |
| 短时操作测量 |
多次运行取平均值(避免单次误差) |
| Web API响应监控 |
在中间件中集成Stopwatch,记录接口耗时 |
| 算法效率对比 |
结合GC.Collect()和GC.WaitForPendingFinalizers()排除GC干扰 |
| 高频调用代码分析 |
使用ElapsedTicks替代毫秒计数(更高精度) |