{
In my previous article on delegates in C#, I gave the syntactical basics of delegates as well as a very rough idea of how they could be used in a callback scenario. This time I'd like to revisit the concept of delegates but take it a step further and show you how the .NET Framework wraps the ability to make asynchronous calls right into the capabilities of delegates. In short, once an instance of a delegate is obtained, it comes with BeginInvoke(...) and EndInvoke(IAsyncResult) methods that can be used to piece together an asynchronous call.
In my previous example, we used a class called DB that had a method called RunLongQuery to fake the notion of something that takes a long time. However, it's not difficult to write code that chews away CPU and I/O cycles so this time around we'll use a class that really does take some time to do its work:
public class FileMapper{
public string MapHeirarchy(string startPoint, int thresh, bool top){...}
}
Our class, FileMapper, has a method called MapHeirarchy which takes a directory path, a threshold, and a boolean flag as its respective arguments. The overall goal of this method is simple: you pass it a path and it will graphically map out the directory structure. The integer and boolean arguments are there because the method is called recursively. So if I wrote the following code:
FileMapper map = new FileMapper();
map.MapHeirarchy("C:\\CODE", 0, true);
I'd get a list of the folders underneath the code directory returned as a string. It would look like:
Mapping C:\CODE
-PROJA
--PROJASUB
--PROJASUB2
-PROJB
--PROJBSUB
--PROJBSUB2
Not the most exciting thing in the world, but it can take a long time. For example, to map my C:\Program Files directory took 1 minute and 58 seconds on my Thinkpad. If we have methods such as this, which will take some time to run their course, they are good candidates for an asynchronous call. Asynchronous, in this case, means that once the method call is made, our code will continue executing until we check to see that the method has completed. We don't have to write a lick of thread management code in order to get this behavior; it's all built in for us as long as we know how to define a delegate.
The syntactical basics of delegates should be straightforward if you saw the earlier article. We'll add a delegate to the FileMapper class so that we can call the MapHeirarchy method asynchronously:
public class FileMapper{
public delegate string MapAsync(string startPoint, int thresh, bool top);
public string MapHeirarchy(string startPoint, int thresh, bool top){...}
}
The MapAsync delegate matches the return value and arguments of the method we intend to use it for, MapHeirarchy. I like to call delegates "typesafe method pointers" and point out that they are "typesafe" because you can only point to a method that has the same return type and arguments.
In the class where we are leveraging the FileMapper class, we create an anonymous instance of the class to map the MapHeirarchy method of the instance to an instance of the MapAsync delegate. Sounds like a lot, but it's really just a single line of code:
FileMapper.MapAsync mp = new FileMapper.MapAsync(new FileMapper().MapHeirarchy);
In order to make the asynchronous call, rather than invoking the delegate outright like this:
mp("C:\\DIR", 0, true);
we will use the BeginInvoke method of the delegate like this:
IAsyncResult v = mp.BeginInvoke("C:\\Code05", 0, true, null, null);
From left to right, an object which implements the IAsyncResult interface is returned from an asynchronous call. This is, quite simply, the object which we can "ask" about when the method has finished executing and which will allow us to retrieve the results of the method's execution. The mp.BeginInvoke is the call to our delegated method along with the first three arguments which we've seen defined above. The last two arguments, passed as nulls in this instance can be ignored; they must be a part of the call, but we don't have to know what they are just yet.
The IAsyncResult object has an IsCompleted property which holds a boolean flag indicating whether the method has completed. We can write simple code to poll this property until the method has finished its execution:
while(!v.IsCompleted){
Console.WriteLine("Method still executing... ");
}
Under normal circumstances you may take advantage of a loop like this to display or draw an animation that allowed a user to know that processing was taking place. There are many different directions that this could be taken but it suffices to say that at this point the method you've invoked is executing asynchronously and you're free to carry on with whatever code follows it.
To retrieve the result from the method, we must again ask the IAsyncResult object. Our code may look as follows:
if(v.IsCompleted){
string tmp = mp.EndInvoke(v);
Console.Write(tmp);
}
Notice that after the IsCompleted method returns a true value we have the ability to use our delegate and call the EndInvoke method, passing it the IAsyncResult object to get our return value. The implementation is super slick here because the return type of the EndInvoke method is the return type of the original method!
So here we see delegates one step further: the ability to make an asynchronous call with a delegate without having to worry about Threads, Synchs, Timers, or Locks. We also can see a way in which any method has the potential to be made asynchronous, as long as it has an associated delegate to go along with it. Finally, remember that you can define your own delegates for any method. The delegate that allows you to make an asynchronous call does not have to be defined in the same class whose methods you want to call asynchronously.
Download the full code listing here.
Questions? david DOT(.) seruyange AT(@) gmail DOT(.) com
}
Tuesday, April 26, 2005
Thursday, April 21, 2005
WinForms ComboBox ItemData
{
For the most part, I work with web based applications, in which a combo box works as you would expect. The class System.Web.UI.WebControls.DropDownList has an ListItemsCollection which consists of ListItem types. So you can dump data into it using the ListItem class which accepts, in the constructor, arguments for text/value pairs. Your code would look something like this:
ListItem li = new ListItem("MyText", "MyValue");
MyDropDownList.Items.Add(li);
You can shorten it with an anonymous instance of the ListItem:
MyDropDownList.Items.Add(new ListItem("MyText", "MyValue"));
Hence, a lot of code people write to populate these from a database looks like this:
// code to connect, build command, etc...
SqlDataReader rsData = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while(rsData.Read()){
MyDropDownList.Items.Add(new ListItem(rsData["TextFld"].ToString(), rs["ValueFld"].ToString()));
}
rsData.Close(); // never forget to close
Another way to do the same thing:
//code to connect, build command, etc...
SqlDataReader rsData = cmd.ExecuteReader(CommandBehavior.CloseConnection);
MyDropDownList.DataTextField = "TextFld";
MyDropDownList.DataValueField = "ValueFld";
MyDropDownList.DataSource = rsData;
MyDropDownList.DataBind();
rsData.Close(); // never forget to close
Whichever your fancy, the bottom line is that the DropDownList behaves as you expect it to: some text that displays to the user and an associated value which you can reference programmatically.
In the Windows.Forms namespace, used for so called WinForms applications, the class System.Windows.Forms.ComboBox is the equivalent of the drop down list. However, the collection it uses for its items is different; it only seems to have a single value associated with it. How does one map text/value pairs?
The underlying collection used by the WinForms ComboBox is of type ObjectCollection. Not too surprisingly, this is a collection of objects. You can, therefore, store any type of object as an item in this drop down list. For example, if I was keeping track of customer statuses, I may have the following data in a lookup table:
I want a drop down with the status name for the user, but in my code I really care about the status code, not the name. The first thing I can do is build an object to reflect these values:
class StatusData{
private string _StatusName;
private string _StatusValue;
public property StatusName{
get{return _StatusName;}
set{_StatusName = value;}
}
public property StatusValue{
get{return _StatusValue;}
set{_StatusValue = value;}
}
}
I want to add these objects to the drop down, but I want the status name to be displayed. This is the purpose of the DisplayMember property of the System.Windows.Forms.ComboBox class. It will allow me to specify a property to utilize for display on an object that it's storing:
MyWinFormsDropDown.DisplayMember = "StatusName";
After I've specified that, I can then store all my Status objects as references in the ObjectCollection of the WinForms drop down list:
// build collection earlier
foreach(StatusData sd in StatusDataFromDB){
MyWinFormsDropDown.Items.Add(sd);
}
Because I've already assigned the DisplayMember property, it knows which member of the hosted object to use to display. So how do I retrieve the value of the object StatusData that I've stored? Easy: cast the selected item from the drop down list, and then get any property that you want (in this case, we'll get the value).
// in an event somewhere
StatusData s = (StatusData)MyWinFormsDropDown.SelectedItem;
string val = s.StatusValue;
Seems like a massive chore, but if you think about it, there are already text/value datastructures that you can use without having to build unique classes for everything you want in a drop down list. For example, the handy System.Collections.DictionaryEntry would suffice. Try this on for size:
DictionaryEntry d = new DictionaryEntry();
d.Key = "TestName";
d.Value = "TestValue";
MyWinFormsDropDown.DisplayMember = "Key";
MyWinFormsDropDown.Items.Add(d);
Now, rather than building your own custom classes, you can use this provided class instead. But, the more thinking you do about it, the more you'll realize that this is a bit more flexible functionality in the System.Windows.Forms.ComboBox than in its System.Web.UI.DropDownList cousin. Being able to reference an object with any number of values based on what is in a combobox is, to use some street vernacular, "sick." (A good thing).
}
For the most part, I work with web based applications, in which a combo box works as you would expect. The class System.Web.UI.WebControls.DropDownList has an ListItemsCollection which consists of ListItem types. So you can dump data into it using the ListItem class which accepts, in the constructor, arguments for text/value pairs. Your code would look something like this:
ListItem li = new ListItem("MyText", "MyValue");
MyDropDownList.Items.Add(li);
You can shorten it with an anonymous instance of the ListItem:
MyDropDownList.Items.Add(new ListItem("MyText", "MyValue"));
Hence, a lot of code people write to populate these from a database looks like this:
// code to connect, build command, etc...
SqlDataReader rsData = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while(rsData.Read()){
MyDropDownList.Items.Add(new ListItem(rsData["TextFld"].ToString(), rs["ValueFld"].ToString()));
}
rsData.Close(); // never forget to close
Another way to do the same thing:
//code to connect, build command, etc...
SqlDataReader rsData = cmd.ExecuteReader(CommandBehavior.CloseConnection);
MyDropDownList.DataTextField = "TextFld";
MyDropDownList.DataValueField = "ValueFld";
MyDropDownList.DataSource = rsData;
MyDropDownList.DataBind();
rsData.Close(); // never forget to close
Whichever your fancy, the bottom line is that the DropDownList behaves as you expect it to: some text that displays to the user and an associated value which you can reference programmatically.
In the Windows.Forms namespace, used for so called WinForms applications, the class System.Windows.Forms.ComboBox is the equivalent of the drop down list. However, the collection it uses for its items is different; it only seems to have a single value associated with it. How does one map text/value pairs?
The underlying collection used by the WinForms ComboBox is of type ObjectCollection. Not too surprisingly, this is a collection of objects. You can, therefore, store any type of object as an item in this drop down list. For example, if I was keeping track of customer statuses, I may have the following data in a lookup table:
CUSTOMERSTATUSES | |
STATUSNAME | STATUSCODE |
ACTIVE LOAN | ACT |
PAST DUE | DEL |
I want a drop down with the status name for the user, but in my code I really care about the status code, not the name. The first thing I can do is build an object to reflect these values:
class StatusData{
private string _StatusName;
private string _StatusValue;
public property StatusName{
get{return _StatusName;}
set{_StatusName = value;}
}
public property StatusValue{
get{return _StatusValue;}
set{_StatusValue = value;}
}
}
I want to add these objects to the drop down, but I want the status name to be displayed. This is the purpose of the DisplayMember property of the System.Windows.Forms.ComboBox class. It will allow me to specify a property to utilize for display on an object that it's storing:
MyWinFormsDropDown.DisplayMember = "StatusName";
After I've specified that, I can then store all my Status objects as references in the ObjectCollection of the WinForms drop down list:
// build collection earlier
foreach(StatusData sd in StatusDataFromDB){
MyWinFormsDropDown.Items.Add(sd);
}
Because I've already assigned the DisplayMember property, it knows which member of the hosted object to use to display. So how do I retrieve the value of the object StatusData that I've stored? Easy: cast the selected item from the drop down list, and then get any property that you want (in this case, we'll get the value).
// in an event somewhere
StatusData s = (StatusData)MyWinFormsDropDown.SelectedItem;
string val = s.StatusValue;
Seems like a massive chore, but if you think about it, there are already text/value datastructures that you can use without having to build unique classes for everything you want in a drop down list. For example, the handy System.Collections.DictionaryEntry would suffice. Try this on for size:
DictionaryEntry d = new DictionaryEntry();
d.Key = "TestName";
d.Value = "TestValue";
MyWinFormsDropDown.DisplayMember = "Key";
MyWinFormsDropDown.Items.Add(d);
Now, rather than building your own custom classes, you can use this provided class instead. But, the more thinking you do about it, the more you'll realize that this is a bit more flexible functionality in the System.Windows.Forms.ComboBox than in its System.Web.UI.DropDownList cousin. Being able to reference an object with any number of values based on what is in a combobox is, to use some street vernacular, "sick." (A good thing).
}
Thursday, April 14, 2005
Beginning with delegates in C#
{
Delegates are one of the mind benders of the .NET framework. Not once have I been teaching the concept without feeling a bit of inadequacy at drawing up a clear and concise explanation. Unless a developer has worked with function pointers in C, they are a new concept. I'll start off with the most basic example of delegate usage, after which I'll write some posts with some more novel ways to leverage them.
If I wrote the following line of code, you'd know what I mean:
int x = 3;
We've declared a variable x and it points to the value "3" on the stack.
If I wrote the next line of code, you'd also know what was meant:
Foo f = new Foo();
We've declared a variable "f" and pointed it to an instance of the class "Foo" on the heap.
I'm stating the obvious because if the previous two lines of code are understandable, you already know the very first thing about delegates: a delegate is a type, just like our primitive int or our class "Foo." The next leap that must be made is that a delegate points to a method somewhere in your code.
Let's expand the definition of the class Foo:
class Foo{
public void FooMeth(){ ... }
}
Is it possible to make a reference to the instance method FooMeth() somewhere in code? Absolutely, my good man; that's what the delegate is for. To make a reference to this method, first define a delegate somewhere:
public delegate void MethPointer()
Let's break that down: public - access modifier, accessible anywhere, void - this is a delegate that will point to a method that returns no value, MethPointer - this is the name of our delegate, and finally () - the empty parenthesis mean that the delegate will point to a method with no parameters. Delegates are typesafe method pointers - the typesafe part is what you see in the definition: our delegate can only point to a method with an identical return value and parameter list.
So now that we've defined our delegate, how do we create an instance that points to the instance method FooMeth() as we mentioned previously? FooMeth() is an instance method, which means that we need an instance of the class first:
Foo f = new Foo();
Now we create a reference to the delegate in the following manner:
MethPointer d = new MethPointer(f.FooMeth);
We pass the method to the constructor of the delegate. All the hard work is done. To invoke the delegated method, we simply do the following:
d();
One more thing about syntax and then I'll introduce a first basic usage for delegates. If you got this far and you're doing okay, this is a side note you should be able to figure out on your own. A lot of the time, a delegate is declared as a public variable in a class. Something like the following:
public class MyClass{
public delegate void Del();
}
So the line above when we created a reference to it comes to us often as:
MyClass.Del d = new MyClass.Del(f.FooMeth);
This is just a simple matter of understanding that if we declare something inside a class, we need to use the classname to access it.
Now that we've got the syntax for a basic delegate, how on earth does one use such things?
The example I always like to start with is a "callback": you can call a remote method and pass a reference to a method in your local class. For example, let's say we had a database utility class called DB that had a RunLongQuery method. This method runs a query against our database that sometimes takes 0.0005 seconds, but for large reports it can take 5, 10, sometimes 25 seconds. Whenever the query finishes, you'd like to invoke a method from your application.
So I've got a class MyDatabaseApp which makes a call to RunLongQuery from DB. But we can design RunLongQuery to accept a reference to a method as a parameter by first putting a delegate in DB and then using it as a parameter on the method. Check it out:
public class DB{
public delegate void Done();
public void RunLongQuery(string sql, Done cback){
// code from query
cback(); // invoke the callback method
}
}
Let's also look at the internals of the class MyDatabaseApp:
public class MyDatabaseApp{
public void FinishedQuery(){ ... }
public void MyAppQuery(){
DB.Done d = new DB.Done(this.FinishedQuery);
DB dbhelper = new DB();
dbhelper.RunLongQuery("select... ", d);
}
}
When MyAppQuery calls the RunLongQuery method from the instance of DB, dbhelper, it passes a delegate reference to the FinishedQuery method. This will automatically be invoked when the query is completed - see inside the definition of DB to see how it's being invoked.
You can download a VS.NET 2003 example of the following here (~15kb).
These are the very beginnings of delegates - our example is also quite trivial because it's just a teaser for one of the more seductive asynchronous callback that can be done with delegates. But by this time, you should be aware that delegates are types - they can be referenced by variables and to create an instance of a delegate we need first to define the delegate and then pass a method that meets its requirements (return type and signature).
Questions? david DOT(.) seruyange AT(@) gmail DOT(.) com
}
Delegates are one of the mind benders of the .NET framework. Not once have I been teaching the concept without feeling a bit of inadequacy at drawing up a clear and concise explanation. Unless a developer has worked with function pointers in C, they are a new concept. I'll start off with the most basic example of delegate usage, after which I'll write some posts with some more novel ways to leverage them.
If I wrote the following line of code, you'd know what I mean:
int x = 3;
We've declared a variable x and it points to the value "3" on the stack.
If I wrote the next line of code, you'd also know what was meant:
Foo f = new Foo();
We've declared a variable "f" and pointed it to an instance of the class "Foo" on the heap.
I'm stating the obvious because if the previous two lines of code are understandable, you already know the very first thing about delegates: a delegate is a type, just like our primitive int or our class "Foo." The next leap that must be made is that a delegate points to a method somewhere in your code.
Let's expand the definition of the class Foo:
class Foo{
public void FooMeth(){ ... }
}
Is it possible to make a reference to the instance method FooMeth() somewhere in code? Absolutely, my good man; that's what the delegate is for. To make a reference to this method, first define a delegate somewhere:
public delegate void MethPointer()
Let's break that down: public - access modifier, accessible anywhere, void - this is a delegate that will point to a method that returns no value, MethPointer - this is the name of our delegate, and finally () - the empty parenthesis mean that the delegate will point to a method with no parameters. Delegates are typesafe method pointers - the typesafe part is what you see in the definition: our delegate can only point to a method with an identical return value and parameter list.
So now that we've defined our delegate, how do we create an instance that points to the instance method FooMeth() as we mentioned previously? FooMeth() is an instance method, which means that we need an instance of the class first:
Foo f = new Foo();
Now we create a reference to the delegate in the following manner:
MethPointer d = new MethPointer(f.FooMeth);
We pass the method to the constructor of the delegate. All the hard work is done. To invoke the delegated method, we simply do the following:
d();
One more thing about syntax and then I'll introduce a first basic usage for delegates. If you got this far and you're doing okay, this is a side note you should be able to figure out on your own. A lot of the time, a delegate is declared as a public variable in a class. Something like the following:
public class MyClass{
public delegate void Del();
}
So the line above when we created a reference to it comes to us often as:
MyClass.Del d = new MyClass.Del(f.FooMeth);
This is just a simple matter of understanding that if we declare something inside a class, we need to use the classname to access it.
Now that we've got the syntax for a basic delegate, how on earth does one use such things?
The example I always like to start with is a "callback": you can call a remote method and pass a reference to a method in your local class. For example, let's say we had a database utility class called DB that had a RunLongQuery method. This method runs a query against our database that sometimes takes 0.0005 seconds, but for large reports it can take 5, 10, sometimes 25 seconds. Whenever the query finishes, you'd like to invoke a method from your application.
So I've got a class MyDatabaseApp which makes a call to RunLongQuery from DB. But we can design RunLongQuery to accept a reference to a method as a parameter by first putting a delegate in DB and then using it as a parameter on the method. Check it out:
public class DB{
public delegate void Done();
public void RunLongQuery(string sql, Done cback){
// code from query
cback(); // invoke the callback method
}
}
Let's also look at the internals of the class MyDatabaseApp:
public class MyDatabaseApp{
public void FinishedQuery(){ ... }
public void MyAppQuery(){
DB.Done d = new DB.Done(this.FinishedQuery);
DB dbhelper = new DB();
dbhelper.RunLongQuery("select... ", d);
}
}
When MyAppQuery calls the RunLongQuery method from the instance of DB, dbhelper, it passes a delegate reference to the FinishedQuery method. This will automatically be invoked when the query is completed - see inside the definition of DB to see how it's being invoked.
You can download a VS.NET 2003 example of the following here (~15kb).
These are the very beginnings of delegates - our example is also quite trivial because it's just a teaser for one of the more seductive asynchronous callback that can be done with delegates. But by this time, you should be aware that delegates are types - they can be referenced by variables and to create an instance of a delegate we need first to define the delegate and then pass a method that meets its requirements (return type and signature).
Questions? david DOT(.) seruyange AT(@) gmail DOT(.) com
}
Subscribe to:
Posts (Atom)