如何在C#中计算List<T>的字节大小以实现批量发送?
分批发送大型List的实现方案
完全可以通过运行时计算每个对象序列化后的字节大小并累加,来精准控制批次的JSON字节数,避免超过服务端的6Mb限制。以下是具体实现思路和代码改造方案:
核心思路
由于对象存在多层嵌套,直接估算内存占用无法精准对应JSON序列化后的字节数,因此实际序列化单个对象并计算其UTF-8字节数是最可靠的方式。同时要把JSON数组的结构开销(如[]和对象间的逗号)纳入计算,确保最终批次序列化后的总大小不超过预设阈值。
具体实现步骤
- 设定批次字节阈值(建议设为4.8~5Mb,预留少量余量)
- 遍历原List,逐个计算对象序列化后的字节数
- 累加字节数(包含数组结构开销),当加入当前对象会超过阈值时,立即发送当前批次并开启新批次
- 处理遍历结束后剩余的最后一批对象
代码改造示例
基于你现有的Newtonsoft.Json代码,改造如下:
1. 计算单个对象序列化后的字节数(优化版)
避免先序列化字符串再转字节的额外开销,直接通过内存流计算:
private long GetObjectJsonByteSize<T>(T obj) { using (var memoryStream = new MemoryStream()) using (var jsonWriter = new JsonTextWriter(new StreamWriter(memoryStream, Encoding.UTF8))) { var serializer = new JsonSerializer(); serializer.Serialize(jsonWriter, obj); jsonWriter.Flush(); return memoryStream.Length; } }
2. 分批处理与发送逻辑
// 配置参数 var maxBatchBytes = 4_800_000; // 4.8Mb,预留余量避免超限制 var authToken = "你的授权Token"; var targetUrl = "目标Web服务地址"; var currentBatch = new List<T>(); long currentBatchTotalBytes = 2; // 初始为JSON数组的[],占2字节 using (var httpClient = new HttpClient()) { foreach (var item in list) { var itemBytes = GetObjectJsonByteSize(item); // 计算加入当前对象需要增加的字节数:非空批次需加逗号(1字节)+ 对象字节数 var addedBytes = currentBatch.Count > 0 ? 1 + itemBytes : itemBytes; if (currentBatchTotalBytes + addedBytes <= maxBatchBytes) { currentBatch.Add(item); currentBatchTotalBytes += addedBytes; } else { // 发送当前批次 await SendBatch(currentBatch, targetUrl, authToken, httpClient); // 重置批次,加入当前对象 currentBatch.Clear(); currentBatch.Add(item); currentBatchTotalBytes = 2 + itemBytes; // [] + 当前对象字节数 } } // 发送最后一批剩余对象 if (currentBatch.Count > 0) { await SendBatch(currentBatch, targetUrl, authToken, httpClient); } } // 封装批次发送方法 private async Task SendBatch<T>(List<T> batch, string url, string authToken, HttpClient client) { var jsonContent = JsonConvert.SerializeObject(batch); var request = new HttpRequestMessage(HttpMethod.Post, url) { Content = new StringContent(jsonContent, Encoding.UTF8, "application/json") }; request.Headers.Add("Authorization", $"Bearer {authToken}"); var response = await client.SendAsync(request); // 可根据需求添加响应校验或处理逻辑 response.EnsureSuccessStatusCode(); var responseJson = await response.Content.ReadAsStringAsync(); // 这里可以记录日志、处理返回结果等 }
注意事项
- 结构开销计算:JSON数组的
[]占2字节,每个对象之间的逗号占1字节,这些必须纳入总字节数计算,否则会出现实际序列化后超标的情况。 - 性能优化:使用内存流计算字节数的方式,比先序列化字符串再计算更高效,尤其适合大量对象的场景。
- 阈值预留:不要把阈值设为刚好5Mb,预留200KB左右的余量,避免因序列化时的细微差异(如空格、转义字符)导致超限制。
内容的提问来源于stack exchange,提问作者WorkJ
相关产品推荐
相关产品推荐

