2

I've created a wrapper class around a FileSystemWatcher. The purpose of the class is to wait for a text file to be created and then read the file's contents into its _result field.

I would like to add a method to the class called GetResultAsync() so the caller can await for the result to be ready, but I don't know how best to go about it. Please would someone point me in the right direction?

internal class SingleFileWatcher : IDisposable
{
    private readonly string _targetFile;
    private readonly FileSystemWatcher _fileWatcher;
    private string _result = null;

    internal SingleFileWatcher(string theFile)
    {
        _targetFile = theFile;

        _fileWatcher = new FileSystemWatcher();
        _fileWatcher.Path = Path.GetDirectoryName(theFile);
        _fileWatcher.Filter = Path.GetFileName(theFile);
        _fileWatcher.IncludeSubdirectories = false;
        _fileWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName;
        _fileWatcher.Created += FileCreated;

        if (File.Exists(theFile))
        {
            ProcessFile();
        }
        else
        {
            //this must happen after other members have been set
            _fileWatcher.EnableRaisingEvents = true;
        }
    }

    internal Task<string> GetResultAsync()
    {
        //todo
    }

    private void FileCreated(Object sender, FileSystemEventArgs e)
    {
        ProcessFile();
    }

    private void ProcessFile()
    {

        FileStream stream = null;

        //filecreated is raised as soon as the file is created, but the process may still be writing to the file
        while (true)
        {
            try
            {
                stream = new FileStream(_targetFile, FileMode.Open, FileAccess.Read);

                using (var reader = new StreamReader(stream))
                {
                    stream = null;
                    _result = reader.ReadToEnd();
                }

                break;
            }
            catch (IOException)
            {
                Thread.Sleep(TimeSpan.FromMilliseconds(500));
            }
            finally
            {
                stream?.Dispose();
            }
        }
    }

    public void Dispose()
    {
        _fileWatcher.Created -= FileCreated;
        _fileWatcher.Dispose();
    }
}
Col098
  • 53
  • 8

1 Answers1

1

Extending Jeroen Mosterts comment. Your code should look like this:

internal Task<string> GetResultAsync()
{
    var tcs = new TaskCompletionSource<string>();
    ProcessFile();
    tcs.TrySetResult(_result);
    return tcs.Task;
}

It is very naive and I suggest you think about the scenarios with exceptions handling and cancellation. They should be inside ProcessFile so it will look like Task<string> ProcessFile(TaskCompletionSource<string> tcs). To set exception use tcs.TrySetException(exception).

xneg
  • 1,204
  • 15
  • 24