Mr Heelis
Mr Heelis

Reputation: 2546

BlockingCollection worker needs to return a value via anonymous function

I have a tricky situation and was wondering if anyone could shed any light on the matter:

I have a blocking collection action worker that is called like this

 ultraHash hash = new ultraHash(lblFolder.Text, (Action) (() => {
       textBox1.Text = self.getMD5();
 });
 runQueue.addAction(hash);

now the "self" thing is there is pseudo code, and this is why, this is the worker (where runQueue is the BlockingCollection container class)

class ultraHash
{
    string _filePath;
    Action _onComplete;
    string _md5;
    public ultraHash(string filePath)
    {
        _filePath = filePath;
    }

    public ultraHash(string filePath, Action onComplete) : this(filePath) //constructor chaining
    {
        _onComplete = onComplete;
    }

    public override void action()
    {
        using (var md5 = MD5.Create())
        {
            try
            {
                using (var stream = File.OpenRead(_filePath))
                {
                    _md5 = MakeHashString(md5.ComputeHash(stream));
                    if (_onComplete != null) _onComplete();
                }
            }
            catch (IOException)
            {
                /***
                 * file is Busy
                 */
            }
        }
    }

    public string getMD5()
    {
        return _md5;
    }

    private string MakeHashString(byte[] hashBytes)
    {
        StringBuilder hash = new StringBuilder(32);
        foreach (byte b in hashBytes)
        {
            hash.Append(b.ToString("X2").ToLower());
        }
        return hashBytes.ToString();
    }
}

now what I want to happen is for the anonymous method Action (() => {} in the first snippet to execute after the queue has completed and be able to contain the MD5 sum. I know there is thread safety issues here, so I understand I have to invoke this back to the parent thread where textBox1 resides, but at the moment I am just lost as to how to do this (is it even possible?)

EDIT

I don't normally "edit" answers but in case anyone else hits this issue this is the code I eventually used, thanks to the answer from usr. Notice the BeginInvoke

  ultraHash hash = null;
  hash = new ultraHash(lblFolder.Text, (Action) (() => {
       this.BeginInvoke((Action) (() => {
            txtMain.Text = hash.getMD5();
        }));
  }));
  runQueue.addAction(hash);

also, if you are wondering why I did this, it is merely so that I can "watch" a folder for any file changes and then store meta data changes, since entire folders can be dragged in/out there needs to be a queueing mechanism in a FIFO queue, also the parent folders are hashed too, so any file alterations need to bubble up, lastly it may be that a file is locked when it is trying to be hashed in which case a FIFO queue can wait

Upvotes: 3

Views: 305

Answers (1)

usr
usr

Reputation: 171178

Probably, you should pass the hash as an argument to your onComplete function. getMD5 does not need to exist.

If you insists on doing it just this way you need a little initialization dance:

ultraHash hash = null;
hash = new ultraHash(lblFolder.Text, (Action) (() => {
           textBox1.Text = hash.getMD5();
     });

The whole design of the ultraHash seems strange. Is it really necessary for that class to exist? A single static method that computes the hash should be enough.

Upvotes: 1

Related Questions