传递MemoryStream作为参数:复用原对象还是创建新实例?
问题
从OneDrive下载文件得到对应的MemoryStream,重新上传前要在内存中对文件做若干处理。为精简控制器代码,把处理逻辑拆成了独立函数,现在有几个疑问:
- 将
MemoryStream作为参数传给这些函数时,函数内对该流的修改是作用于内存中同一个对象,还是会创建副本导致修改无效?既不想创建重复内存流,也担心ASP.NET Core中出现内存泄漏。 - 考虑传递
WordprocessingDocument作为参数,同样不清楚这样是创建副本还是复用同一个对象。
代码示例:
public ActionResult FinalizeDoc() { using (var memoryStream = await oneDrive.DriveItemDownloadAsync(SharedDriveID, OneDrivePath, "path")) { DeleteComments(memoryStream); AcceptTrackChanges(memoryStream); var UploadDoc = await oneDrive.UploadFileStreamAsync(SharedDriveID, memoryStream, fullPath, "replace"); } } public void DeleteComments(MemoryStream memoryStream) { using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(memoryStream, true)) { // 删除批注代码 } } public void AcceptTrackChanges(MemoryStream memoryStream) { using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(memoryStream, true)) { // 接受修订代码 } }
回答
关于MemoryStream的参数传递
- 复用同一个内存对象,不会创建副本:C#中引用类型(包括
MemoryStream)默认按引用传递,函数内操作的就是原MemoryStream对象本身,所有修改都会直接作用于原流,不会产生副本,不用担心修改无效的问题。 - 必须重置流的位置:当前代码存在隐患——每次通过
WordprocessingDocument.Open处理完后,流的Position会停在末尾。如果直接传给下一个函数或上传方法,后续操作会从流的末尾开始读取,导致读取不到有效内容。所以每次处理完后需要手动重置流的位置:
上传前也要确保流的位置在开头,否则会上传空内容。public void DeleteComments(MemoryStream memoryStream) { using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(memoryStream, true)) { // 删除批注代码 } memoryStream.Position = 0; // 重置流位置到开头 } - 内存泄漏风险极低:代码中
memoryStream用using语句包裹,会自动释放资源,ASP.NET Core的GC也会正常回收内存。只要确保所有流和WordprocessingDocument都用using正确释放,就不会出现内存泄漏问题。
关于传递WordprocessingDocument的可行性
- 同样复用同一个对象:
WordprocessingDocument属于引用类型,传递参数时不会创建副本,函数内的操作会直接作用于原对象。 - 不建议这么做:
WordprocessingDocument和底层流强绑定,用using包裹时会自动关闭流(即使指定leaveOpen: true也需要额外注意生命周期)。如果把WordprocessingDocument传给多个函数,很容易出现流被提前关闭、重复操作已释放资源的问题,反而增加代码复杂度。保持当前“传递流+每次打开处理+重置位置”的模式更稳妥。
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

