There are several ways that operations can take place asynchronously in .NET. Since you didn't post any specifics, I'll give an overview of the more common patterns:
AsyncWaitHandle
Often you'll find (especially in the .NET framework classes) pairs of methods named with Begin and End (i.e. something like BeginReceive and EndReceive).
Calling the begin function returns an AsyncWaitHandle object, which can be used to synchronize threads like a regular WaitHandle (by calling WaitOne or passing it into the static WaitHandle.WaitAny or WaitHandle.WaitAll functions), then is passed into the corresponding "end" function to obtain the return value of the function (or, if one occurred, throw the exception that was encountered).
If you use the simple .NET facility for calling a method asynchronously (creating a delegate for the function and calling BeginInvoke and EndInvoke), then this is the approach you'll need to use. However, most API's that have built-in support for asynchronous operations will handle the actual asynchronous calling part for you, leaving you with properly named BeginXXX and EndXXX functions, rather than forcing you to create a delegate yourself.
Sample using the WaitHandle:
IAsyncResult result = object.BeginOperation(args);
result.AsyncWaitHandle.WaitOne();
// that will cause the current thread to block until
// the operation completes. Obviously this isn't
// exactly how you'd ordinarily use an async method
// (since you're essentially forcing it to be
// synchronous this way)
returnValue = object.EndOperation(result);
Often async methods are coupled with events, as shown below.
Events
Sometimes a method call that completes asynchronously will raise an event upon its completion. This is also sometimes used in combination with the AsyncWaitHandle approach to deliver the notification that the process has completed. For example, the MSMQ libraries have a BeginReceive and EndReceieve function on the queue object, as well as a ReceieveCompleted event. When the event fires, it indicates that EndReceive can then be called, passing in the AsyncWaitHandle object returned from the call to BeginReceive. to get the actual message that was received.
Either way, you attach to the event and use it as you would any other event.
Sample using AsyncWaitHandle and events:
(somewhere in the code)
object.OperationComplete += object_OperationComplete;
(elsewhere)
IAsyncResult result = object.BeginOperation(args);
// at this point, you can either wait for the event
// to fire, or use the WaitHandle approach as shown
// in the previous example
...
void objectOperationComplete(object sender, AsyncOperationEventArgs e)
{
returnValue = object.EndOperation(e.AsyncResult);
}
These generally work in a variety of ways...you can hold on to the IAsyncResult returned from the Begin operation yourself, but most libraries will pass that IAsyncResult object as a property on their particular EventArgs class.
It's also common to find the return value being present as a property on the EventArgs class. While using this value is fine, if the Begin operation returned an IAsyncResult, it's always a good idea to call the corresponding End function, even if the data you need is in the EventArgs. The End function is usually the way that exceptions are caught, as well.
Callbacks
A callback (in .NET) is a delegate that is supplied to the asynchronous function. Callbacks are used in more than just asynchronous functions, but in this context they're generally a delegate that you supply when calling the function that it will call when it completes.
Callbacks are similar to events (in that they're both delegate-based), though there is more of a one-to-one correlation between the method calls and the callbacks supplied.
Example using a callback:
object.BeginOperation(args, OperationComplete);
...
void OperationComplete(SomeObject results)
{
...
}