Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Async-Up Your Objects : Page 4

Encapsulate asynchronous functionality directly into your business objects. The .NET Framework facilitates calling object methods asynchronously through the use of delegates. You may already know how to do this using helper code, but there is a cleaner and much cooler way of packaging this kind of functionality right inside your business objects.

Asynchronous Calling (continued)
As you might have guessed, the BeginMoveFiles method is what gets called from a client application, and you'll see how later. Now you have to declare the EndMoveFiles method, which is not the callback but will get used by the callback. The callback is still defined by the client and resides with the client. The EndMoveFiles routine cleans up the delegate when the client calls it from its callback, or from any other function that wants to block the thread and wait for the asynchronous call to finish. In order to wrap up the asynchronous delegate call, you need access to the original delegate used. Let's see the code first.

In C#:

public bool EndMoveFiles(IAsyncResult ar) { MoveFilesDelegate o_MyDelegate = (MoveFilesDelegate)((AsyncResult)ar).AsyncDelegate; return o_MyDelegate.EndInvoke(ar); }


Public Function EndMoveFiles( _ ByVal ar As IAsyncResult) As Boolean Dim o_MyDelegate As MoveFilesDelegate = _ CType( _ CType(ar, AsyncResult).AsyncDelegate, _ MoveFilesDelegate) Return o_MyDelegate.EndInvoke(ar) End Function

The first thing to notice is that the signature for this method starts with the return variable the same type as that of the original MoveFiles method. You will also notice that the only argument is the IAsyncResult object. You are maintaining a naming and signature convention that matches not only the BeginInvoke and EndInvoke, which are part of standard delegates, but also all the custom asynchronous calling functionality used throughout the .NET Framework. There's that long complex line of code again; the one that retrieves our original delegate.

Finally you call the EndInvoke method and send to it the IAsyncResult object that was passed in, at the same time capturing whatever variable was returned and in turn sending that back to whatever called EndMoveFiles (you'll see that in a minute).

True to .NET Framework asynchronous standards, EndMoveFiles returns the data type of the method that was asynchronously called in the first place, and it receives as an argument only the IAsyncResult object. The actual callback routine that you passed into BeginMoveFiles will be defined by the client application and, as the first example, must still adhere to the signature defined by the AsyncCallback delegate.

You now have a method of kicking off the MoveFiles process asynchronously from within the object itself, and you have a method of closing the asynchronous process, also in the object itself. This wraps up the object's functionality; let's go back to the client application and see how to use the new methods to perform the asynchronous call.

In the first example, the client application made all the delegate declarations and invoked them in conjunction with an instance of the FileFunction class. Now you are going to use solely the FileFunction class to handle everything. Take a look at how the client application uses the new-and-improved FileFunction class. (Listing 3 (C#) and Listing 4 (VB .NET)) shows the complete client code as it appears in a button click event on a Windows form.)

In C#:

FileFunctions o_MyFunctions = new FileFunctions(); IAsyncResult o_AsyncResult = o_MyFunctions.BeginMoveFiles( "c:\\Folder1", "c:\\Folder2", new AsyncCallback(MoveFilesComplete_NewWay), null);


Dim o_MyFunctions As FileFunctions = _ New FileFunctions Dim o_AsyncResult As IAsyncResult = _ o_MyFunctions.BeginMoveFiles( _ "c:\\Folder1", "c:\\Folder2", _ New AsyncCallback( _ AddressOf MoveFilesComplete_NewWay), _ Nothing)

As you can see, there is much less delegate code—or at least less "raw" delegate code. The calls made are specific to the b class and the appearance given on the component is a clean and encapsulated design. In fact, it is not mere appearance; the design is functionally much cleaner. It now also resembles a standard .NET Framework component. Ok, let's take this code apart.

The first thing to do is instantiate the FileFunctions class. You can't get around doing that. Instead of declaring a delegate here and wiring the MoveFiles method as before, use the new method that you built into FileFunctions. The first arguments are those that correspond to the original MoveFiles method, followed by the callback method and the AsyncState argument left as null. Also notice that, as before, you are returning an IAsyncResult implementation. If you want to use this return object, scope its declaration at the class level so you can use it outside of this method.

The callback argument is an instantiation of the AsyncCallback delegate wired to the callback method, just like before, only this time you did it in one line of code. This is the reason you did not have to instantiate the AsyncCallback delegate in the BeginMoveFiles method of the business object, but instead used just this callback delegate (received as an argument) directly into the BeginInvoke of the delegate. Let's look at the callback method.

In C#:

private void MoveFilesComplete( IAsyncResult ar) { FileFunctions o_FileFunctions = new FileFunctions(); bool b_Success = o_FileFunctions.EndMoveFiles(ar); // Perform any further functionality. }


Private Sub MoveFilesComplete( _ ByVal ar As IAsyncResult) Dim o_FileFunctions As FileFunctions = _ New FileFunctions Dim b_Success As Boolean = _ o_FileFunctions.EndMoveFiles(ar) ' Perform any further functionality. End Sub

Notice that this method conforms to the signature of the AsyncCallback delegate. In the first example, you retrieved the original delegate from the ar variable and called the EndInvoke method. Here, you just instantiate another instance of the FileFunctions class, call the EndMoveFiles method, and send the IAsyncResult argument you received into it. The IAsyncResult argument contains all the information pertaining to the asynchronous process. Remember from the EndMoveFiles code this is where you retrieve the original delegate and call EndInvoke. This entire delegate plumbing now happens behind the scenes. You need to retrieve the original FileFunctions instance because it was that object that kicked off a new thread, so it is that object you need to close and properly housekeep that thread.

As you can see, it's not that complicated, and you can repeat this methodology for any other methods for which you want to add an asynchronous wrapper. With this technique, you can provide your component's users with more complete and robust objects that also conform to .NET Framework naming and usage conventions.

Miguel A. Castro is President of InfoTek Consulting Group, Inc., a professional consulting firm that specializes in architecting, designing, and developing solutions using Microsoft .NET technologies. His VB background goes all the way back to 1.0. Miguel's focus for the last couple of years has been the .NET Framework and languages; he is fluent in both VB .NET and C#. His programming experience goes back 20 years when he started on TRS-80s, Apple IIs, and Atari 800 computers. Miguel lives in Lincoln Park, New Jersey with his wife Elena and his daughter Victoria.
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date