Closure
Tuesday, 11 May 2010
Article Index
Closure
Two patterns for closure
Event-oriented environment

Using closure 1 - More parameters

Clearly closures are fun but what use are they?

The answer is that they provide a context for a function which can be used to provide it with additional information without the need to use additional parameters.

Why wouldn’t you create some additional parameters?

Most likely because the signature of the function you are trying to use isn’t under your control.

Banner

For example consider the EnumWindows API call which needs a callback function that is called for each window that it enumerates. The API call is:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumWindows(
EnumWindowsProc lpEnumFunc,
ref IntPtr lParam);

and the callback delegate is:

public delegate bool EnumWindowsProc(
IntPtr hWnd,
ref IntPtr lParam);

The problem with using the callback delegate is that it only has the two parameters - the handle of the current window and a pointer supplied in the call to the EnumWindows function.

It is this pointer that is used to communicate between the callback function and the program needing the enumeration.

Closure, however, makes communication much easier.

If you need a function to find a particular dialog box specified by its Owner and its Caption string then you could write a function something like:

public IntPtr getDialog(
IntPtr Owner, String Caption)
{

Clearly we need to pass the Owner and Caption to the callback delegate so that it can compare each of the enumerated windows to the target. The most natural way to do this is to provide these extra pieces of information to the callback via extra parameters but to work as a callback it can only have the parameters defined by the API.

The simplest solution is to define the callback delegate in the usual way but use the fact that the Owner  and Caption parameters are in scope and so are captured by an anonymous function:

EnumWindowsProc enumProc = 
delegate(IntPtr handle,
ref IntPtr pointer)
{

First we get the window text and compare it to Caption, which we can only access here thanks to closure:

 int length = 
GetWindowTextLength(handle);
StringBuilder wTitle =
new StringBuilder(length + 1);
GetWindowText(handle, wTitle,
wTitle.Capacity);
if (wTitle.ToString() == Caption)
{

If they match we check that the class name is correct for a dialog box and then check that Owner, which is once again only accessible at this point because of closure, is the correct window:

  int max = 100;
StringBuilder classname =
new StringBuilder(max);
GetClassName(handle, classname, max);
if (classname.ToString() == "#32770")
{
IntPtr Parent = GetParent(handle);
if (Parent == Owner)
{
pointer = handle;
return false;
}
}
}
return true;
};
This completes the anonymous callback delegate; now we can call EnumWindows:
 IntPtr DlgHwnd = IntPtr.Zero;
EnumWindows(enumProc, ref DlgHwnd);
return DlgHwnd;
}

To follow what is going on here you need to keep in mind that the callback is actually run by the API  via the call

enumProc(handle,pointer);

which passes in the handle to the window and a pointer to soon additional data. The Owner and Caption variables are used within the callback courtesy of closure.

Notice that the pointer in the callback delegate is used to return the handle of the dialog box that we have found but this too could have been achieved using closure.

Without any use of closure we would have had to pack the Owner and Caption into a data structure and passed this to the callback. Closure makes things much simpler in this case.

Banner

<ASIN:1430225491>

<ASIN:0321718933>

<ASIN:0470467274>

<ASIN:0470563486>

<ASIN:1430225254>



Last Updated ( Wednesday, 04 August 2010 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.