Add a DisposableList helper

This commit is contained in:
Ben Olden-Cooligan 2022-06-09 12:09:24 -07:00
parent c9d982a531
commit 5460ceea9f
10 changed files with 67 additions and 91 deletions

View File

@ -34,6 +34,9 @@ public record Memento(ImmutableList<ProcessedImage> Images) : IDisposable
public void Dispose()
{
Images.DisposeAll();
foreach (var image in Images)
{
image.Dispose();
}
}
}

View File

@ -45,6 +45,9 @@ public class AutoSaver
var imageList = new List<ProcessedImage>();
source.ForEach(img =>
{
// TODO: We should assume the returned sink may dispose what we give it, therefore we should make
// a clone before sending it out, and then dispose the clone when we're done with it
// TODO: We should add tests for this class
sink.PutImage(img);
imageList.Add(img);
}).ContinueWith(async t =>

View File

@ -10,7 +10,7 @@ public interface IScannedImagePrinter
/// <param name="images">The full list of images to print.</param>
/// <param name="selectedImages">The list of selected images. If non-empty, the user will be presented an option to print selected.</param>
/// <returns>True if the print completed, false if there was nothing to print or the user cancelled.</returns>
Task<bool> PromptToPrint(List<ProcessedImage> images, List<ProcessedImage> selectedImages);
Task<bool> PromptToPrint(IList<ProcessedImage> images, IList<ProcessedImage> selectedImages);
/// <summary>
/// Prints the provided images with the specified printer settings.
@ -19,5 +19,5 @@ public interface IScannedImagePrinter
/// <param name="images">The full list of images to print.</param>
/// <param name="selectedImages">The list of selected images, to be used if the printer settings specify to print selected.</param>
/// <returns>True if the print completed, false if there was nothing to print.</returns>
Task<bool> Print(PrinterSettings printerSettings, List<ProcessedImage> images, List<ProcessedImage> selectedImages);
Task<bool> Print(PrinterSettings printerSettings, IList<ProcessedImage> images, IList<ProcessedImage> selectedImages);
}

View File

@ -32,7 +32,7 @@ public class SaveImagesOperation : OperationBase
/// <param name="placeholders"></param>
/// <param name="images">The collection of images to save.</param>
/// <param name="batch"></param>
public bool Start(string fileName, Placeholders placeholders, List<ProcessedImage> images, IConfigProvider<ImageSettings> imageSettings, bool batch = false)
public bool Start(string fileName, Placeholders placeholders, IList<ProcessedImage> images, IConfigProvider<ImageSettings> imageSettings, bool batch = false)
{
Status = new OperationStatus
{
@ -123,11 +123,6 @@ public class SaveImagesOperation : OperationBase
Log.ErrorException(MiscResources.ErrorSaving, ex);
InvokeError(MiscResources.ErrorSaving, ex);
}
finally
{
images.ForEach(s => s.Dispose());
GC.Collect();
}
return false;
});
Success.ContinueWith(task =>

View File

@ -8,7 +8,7 @@ namespace NAPS2.ImportExport.Images;
public class TiffHelper
{
public async Task<bool> SaveMultipage(List<ProcessedImage> images, string location, TiffCompression compression, ProgressHandler progressCallback, CancellationToken cancelToken)
public async Task<bool> SaveMultipage(IList<ProcessedImage> images, string location, TiffCompression compression, ProgressHandler progressCallback, CancellationToken cancelToken)
{
try
{

View File

@ -14,7 +14,7 @@ public class PrintDocumentPrinter : IScannedImagePrinter
_imageContext = imageContext;
}
public async Task<bool> PromptToPrint(List<ProcessedImage> images, List<ProcessedImage> selectedImages)
public async Task<bool> PromptToPrint(IList<ProcessedImage> images, IList<ProcessedImage> selectedImages)
{
if (!images.Any())
{
@ -39,9 +39,9 @@ public class PrintDocumentPrinter : IScannedImagePrinter
return false;
}
public async Task<bool> Print(PrinterSettings printerSettings, List<ProcessedImage> images, List<ProcessedImage> selectedImages)
public async Task<bool> Print(PrinterSettings printerSettings, IList<ProcessedImage> images, IList<ProcessedImage> selectedImages)
{
List<ProcessedImage> imagesToPrint;
IList<ProcessedImage> imagesToPrint;
switch (printerSettings.PrintRange)
{
case PrintRange.AllPages:

View File

@ -1,4 +1,6 @@
namespace NAPS2.Util;
using System.Collections.Immutable;
namespace NAPS2.Util;
public static class CollectionExtensions
{
@ -221,11 +223,8 @@ public static class CollectionExtensions
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> enumerable) => enumerable.Where(x => x != null)!;
public static void DisposeAll<T>(this IEnumerable<T?> enumerable) where T : IDisposable
public static DisposableList<T> ToDisposableList<T>(this IEnumerable<T> enumerable) where T : IDisposable
{
foreach(var obj in enumerable)
{
obj?.Dispose();
}
return new DisposableList<T>(enumerable.ToImmutableList());
}
}

View File

@ -0,0 +1,23 @@
using System.Collections.Immutable;
namespace NAPS2.Util;
// TODO: Maybe instead of a generic DisposableList we should create an class for a list of ProcessedImage that has
// explicit ownership semantics
public class DisposableList<T> : IDisposable where T : IDisposable
{
public DisposableList(ImmutableList<T> innerList)
{
InnerList = innerList;
}
public ImmutableList<T> InnerList { get; }
public void Dispose()
{
foreach(var item in InnerList)
{
item.Dispose();
}
}
}

View File

@ -884,59 +884,35 @@ namespace NAPS2.WinForms
private async void SavePDF(List<UiImage> images)
{
var imagesToSave = images.Select(x => x.GetClonedImage()).ToList();
try
using var imagesToSave = images.Select(x => x.GetClonedImage()).ToDisposableList();
if (await _exportHelper.SavePDF(imagesToSave.InnerList, _notify))
{
if (await _exportHelper.SavePDF(imagesToSave, _notify))
if (Config.Get(c => c.DeleteAfterSaving))
{
if (Config.Get(c => c.DeleteAfterSaving))
SafeInvoke(() =>
{
SafeInvoke(() =>
{
_imageList.Mutate(new ImageListMutation.DeleteSelected(), ListSelection.From(images));
});
}
_imageList.Mutate(new ImageListMutation.DeleteSelected(), ListSelection.From(images));
});
}
}
finally
{
// TODO: We probably want a disposable list wrapper (around an immutable list) instead of DisposeAll
imagesToSave.DisposeAll();
}
}
private async void SaveImages(List<UiImage> images)
{
var imagesToSave = images.Select(x => x.GetClonedImage()).ToList();
try
using var imagesToSave = images.Select(x => x.GetClonedImage()).ToDisposableList();
if (await _exportHelper.SaveImages(imagesToSave.InnerList, _notify))
{
if (await _exportHelper.SaveImages(imagesToSave, _notify))
if (Config.Get(c => c.DeleteAfterSaving))
{
if (Config.Get(c => c.DeleteAfterSaving))
{
_imageList.Mutate(new ImageListMutation.DeleteSelected(), ListSelection.From(images));
}
_imageList.Mutate(new ImageListMutation.DeleteSelected(), ListSelection.From(images));
}
}
finally
{
// TODO: We probably want a disposable list wrapper (around an immutable list) instead of DisposeAll
imagesToSave.DisposeAll();
}
}
private async void EmailPDF(List<UiImage> images)
{
var imagesToEmail = images.Select(x => x.GetClonedImage()).ToList();
try
{
await _exportHelper.EmailPDF(imagesToEmail);
}
finally
{
// TODO: We probably want a disposable list wrapper (around an immutable list) instead of DisposeAll
imagesToEmail.DisposeAll();
}
using var imagesToEmail = images.Select(x => x.GetClonedImage()).ToDisposableList();
await _exportHelper.EmailPDF(imagesToEmail.InnerList);
}
private void Import()
@ -1281,19 +1257,11 @@ namespace NAPS2.WinForms
private async void tsPrint_Click(object sender, EventArgs e)
{
var state = _imageList.CurrentState;
var allImages = _imageList.Images.Select(x => x.GetClonedImage()).ToList();
var selectedImages = SelectedImages.Select(x => x.GetClonedImage()).ToList();
try
using var allImages = _imageList.Images.Select(x => x.GetClonedImage()).ToDisposableList();
using var selectedImages = SelectedImages.Select(x => x.GetClonedImage()).ToDisposableList();
if (await _scannedImagePrinter.PromptToPrint(allImages.InnerList, selectedImages.InnerList))
{
if (await _scannedImagePrinter.PromptToPrint(allImages, selectedImages))
{
_imageList.SavedState = state;
}
}
finally
{
allImages.DisposeAll();
selectedImages.DisposeAll();
_imageList.SavedState = state;
}
}
@ -1489,16 +1457,8 @@ namespace NAPS2.WinForms
private async void ctxCopy_Click(object sender, EventArgs e)
{
var imagesToCopy = SelectedImages.Select(x => x.GetClonedImage()).ToList();
try
{
await _imageClipboard.Write(imagesToCopy, true);
}
finally
{
// TODO: We probably want a disposable list wrapper (around an immutable list) instead of DisposeAll
imagesToCopy.DisposeAll();
}
using var imagesToCopy = SelectedImages.Select(x => x.GetClonedImage()).ToDisposableList();
await _imageClipboard.Write(imagesToCopy.InnerList, true);
}
private void ctxPaste_Click(object sender, EventArgs e)
@ -1686,16 +1646,9 @@ namespace NAPS2.WinForms
if (SelectedIndices.Any())
{
var ido = new DataObject();
var selectedImages = SelectedImages.Select(x => x.GetClonedImage()).ToList();
try
{
_imageTransfer.AddTo(ido.ToEto(), selectedImages);
DoDragDrop(ido, DragDropEffects.Move | DragDropEffects.Copy);
}
finally
{
selectedImages.DisposeAll();
}
using var selectedImages = SelectedImages.Select(x => x.GetClonedImage()).ToDisposableList();
_imageTransfer.AddTo(ido.ToEto(), selectedImages.InnerList);
DoDragDrop(ido, DragDropEffects.Move | DragDropEffects.Copy);
}
}

View File

@ -32,7 +32,7 @@ public class WinFormsExportHelper
_uiImageList = uiImageList;
}
public async Task<bool> SavePDF(List<ProcessedImage> images, ISaveNotify notify)
public async Task<bool> SavePDF(IList<ProcessedImage> images, ISaveNotify notify)
{
if (images.Any())
{
@ -63,7 +63,7 @@ public class WinFormsExportHelper
return false;
}
public async Task<bool> ExportPDF(string filename, List<ProcessedImage> images, bool email, EmailMessage emailMessage)
public async Task<bool> ExportPDF(string filename, IList<ProcessedImage> images, bool email, EmailMessage emailMessage)
{
var op = _operationFactory.Create<SavePdfOperation>();
@ -74,7 +74,7 @@ public class WinFormsExportHelper
return await op.Success;
}
public async Task<bool> SaveImages(List<ProcessedImage> images, ISaveNotify notify)
public async Task<bool> SaveImages(IList<ProcessedImage> images, ISaveNotify notify)
{
if (images.Any())
{
@ -108,7 +108,7 @@ public class WinFormsExportHelper
return false;
}
public async Task<bool> EmailPDF(List<ProcessedImage> images)
public async Task<bool> EmailPDF(IList<ProcessedImage> images)
{
if (!images.Any())
{