6
\$\begingroup\$

Here is my effort to implement extensions for FileInfo object:

public static class FileExtensions
{
    public static async Task MoveFileAsync(this FileInfo file, string destinationPath, string destinationFileName = "")
    {
        await CopyFileAsync(file, destinationPath, destinationFileName).ContinueWith((x) =>
        {
            DeleteFileAsync(file);
        }, TaskContinuationOptions.OnlyOnRanToCompletion);
    }

    public static async Task CopyFileAsync(this FileInfo file, string destinationPath, string destinationFileName = "")
    {
        if (string.IsNullOrWhiteSpace(destinationFileName))
        {
            destinationFileName = file.Name;
        }

        using (FileStream SourceStream = file.Open(FileMode.Open))
        {
            using (FileStream DestinationStream = File.Create(Path.Combine(destinationPath, destinationFileName)))
            {
                await SourceStream.CopyToAsync(DestinationStream);
            }
        }
    }

    public static Task DeleteFileAsync(this FileInfo file)
    {
        return Task.Run(() =>
        {
            if (File.Exists(file.FullName))
            {
                File.Delete(file.FullName);
            }
        });
    }
}

The idea was to make it efficient, and basically, allow deleting in the background.

It feels like I mix things, but not sure what exactly is wrong.

So, I have a couple of questions for the beginning:

  1. The implementation of MoveFileAsync:

    • Is this the right way to implement it? I wanted to call the CopyFileAsync and when it is completed -> call DeleteFileAsync.
    • Do I need to add return before DeleteFileAsync(file);? (So that the method will return a task).
  2. The next step is using this DeleteFileAsync method within a loop. Like this:

public Task EmptyWorkingFolder()
{
    var directory = new DirectoryInfo(/*my-directory-path*/);
    var deleteTasks = new List<Task>();

    foreach (FileInfo file in directory.EnumerateFiles())
    {
        deleteTasks.Add(file.DeleteFileAsync());
    }

    return Task.WhenAll(deleteTasks);
}

Now my question is, how useful is this in comparison to just deleting the files synchronously?

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Few tips: 1) if (File.Exists(file.FullName)) a redundant check, look into File.Delete documentation. 2) there's no sense to make disk operations concurrent because file system anyway does all the jobs sequentially for single drive. Run one Task for all the Delete operations if you need to await it. Measure and compare the performance of the different implementations to be sure. 3) don't mix continuations with async/await approach. 4) names for the local variables must begin with lower-cased letter. \$\endgroup\$
    – aepot
    Commented Dec 28, 2021 at 23:21
  • \$\begingroup\$ Thanks for the tips @aepot. Could you please explain more about number 3? \$\endgroup\$
    – SeReGa
    Commented Dec 29, 2021 at 18:45
  • 2
    \$\begingroup\$ For example this await CopyFileAsync(file, destinationPath, destinationFileName).ContinueWith((x) => { DeleteFileAsync(file); }, TaskContinuationOptions.OnlyOnRanToCompletion); can be this await CopyFileAsync(file, destinationPath, destinationFileName); DeleteFileAsync(file); with exactly the same behavior. Continuations and async/await provides the same features but simply looks differently. Mixed code is harder to read. \$\endgroup\$
    – aepot
    Commented Dec 29, 2021 at 22:13
  • \$\begingroup\$ @aepot, now I see how it mixed, and I understand the example, however, how I will have to use 2 awaits in a row (for copy and for delete). So I think that it's better to use Task.WhenAll() to prevent losing errors. \$\endgroup\$
    – SeReGa
    Commented Dec 30, 2021 at 5:42
  • \$\begingroup\$ @aepot, those comments seem to be reviewing the code, and therefore better presented using an answer. And you'll earn reputation points that way! \$\endgroup\$ Commented Mar 22, 2022 at 7:43

1 Answer 1

1
\$\begingroup\$

A move operation is not a copy+delete. When you are moving in a volume the operation is actually a rename under the hood and it happens instantly because there is no data moved.

When copying to a different volume a move is more like a copy+delete except that it will never leave the old file behind where your method will. If you can read the source and not write to it, you will end up with two files.

The other issue I am seeing is that your are calling a synchronous delete in an async method, with the Task.Run and this is a violation of the async contract. While I do have an async delete method, let's skip that because of the first issue here which is that a move is not a copy+delete.

Take a look at the code here as it alleviates some of these concerns: Async file copy/move

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.