views:

124

answers:

4

I am getting familiar with C# day by day and I came across this piece of code

public static void CopyStreamToStream(
Stream source, Stream destination, 
Action<Stream,Stream,Exception> completed) {
byte[] buffer = new byte[0x1000];
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);

Action<Exception> done = e => {
    if (completed != null) asyncOp.Post(delegate { 
        completed(source, destination, e); }, null);
};

AsyncCallback rc = null;
rc = readResult => {
    try {
        int read = source.EndRead(readResult);
        if (read > 0) {
            destination.BeginWrite(buffer, 0, read, writeResult => {
                try {
                    destination.EndWrite(writeResult);
                    source.BeginRead(
                        buffer, 0, buffer.Length, rc, null);
                }
                catch (Exception exc) { done(exc); }
            }, null);
        }
        else done(null);
    }
    catch (Exception exc) { done(exc); }
};

source.BeginRead(buffer, 0, buffer.Length, rc, null);

}

From this article Article

What I fail to follow is that how does the delegate get notified that the copy is done? Say after the copy is done I want to perform an operation on the copied file.

And yes I do know that this may beyond me given my few years in C#.

+2  A: 

The delegate gets wrapped in another delegate, called done. This is called in the catch blocks, as well as in the else block towards the end of the AsyncCallback delegate, that in turn is passed to BeginRead:

AsyncCallback rc = null;
rc = readResult => {
    try {
        int read = source.EndRead(readResult);
        if (read > 0) {
            destination.BeginWrite(buffer, 0, read, writeResult => {
                try {
                    destination.EndWrite(writeResult);
                    source.BeginRead(
                        buffer, 0, buffer.Length, rc, null);
                }
                catch (Exception exc) { done(exc); }  // <-- here
            }, null);
        }
        else done(null);   // <-- here
    }
    catch (Exception exc) { done(exc); }   // <-- and here
};
Fredrik Mörk
+5  A: 

The

done(exc);

and

else done(null);

bits execute the Action<Exception> which in turn will call the Action<Stream, Stream, Exception> passed into it using the completed parameter.

This is done using AsyncOperation.Post so that the completed delegate is executed on an appropriate thread.

EDIT: You'd use it something like this:

CopyStreamToStream(input, output, CopyCompleted);
...

private void CopyCompleted(Stream input, Stream output, Exception ex)
{
    if (ex != null)
    {
        LogError(ex);
    }
    else
    {
        // Put code to notify the database that the copy has completed here
    }
}

Or you could use a lambda expression or anonymous method - it depends on how much logic you need.

Jon Skeet
Jon: If say I wanted to notify a DB that copy is done how would I set that in this code?
ltech
@ltech: You'd pass in an appropriate action (which notified the DB) as the argument for the `completed` parameter.
Jon Skeet
Jon: so would this changeAction<Exception> done = e => { if (completed != null) asyncOp.Post(delegate { completed(source, destination, e); }, null);};to Action<Exception> done = e => { if (completed != null) asyncOp.Post(delegate { completed(source, destination, e,DBWork()); }, null);};
ltech
Jon: I follow now. This is a beautiful piece of code.
ltech
@ltech: Just for the sake of anyone else, you wouldn't *change* the method at all. You'd pass the relevant action *into* it, which would react to either success or failure based on whether an exception was thrown.
Jon Skeet
Jon: would be much obliged if you can provide snippet of above comment by you.
ltech
@ltech: I've added sort of sample code.
Jon Skeet
+1  A: 

The lines done(...) is where the delegate is being raised. The delegate is assigned earlier in the code i.e.

Action<Exception> done = e => {  
    if (completed != null) asyncOp.Post(delegate {   
        completed(source, destination, e); }, null);  
};  
James
A: 

@ltech - Can i use this to copy multiple files across a server? example: in a for loop Can i call this method and would it be the same as instantiating threads or invoking delegate via command pattern to move multiple files?

uno