The .Net framework has had closures (anonymous delegates) for quite a while, but it never occurred to me how I could utilize them until I was exposed to Ruby. Ruby has a beautifully clear and concise syntax for passing a closure to a method, and its libraries make extensive use of them.
The .Net languages I use (c# and Chrome) are statically typed, which introduces some restrictions and syntactic overhead that Ruby doesn't have. But both c# (3.0) and Chrome are fairly good at type inference, so the overhead isn't as high as you might think. Some examples of how my Ruby-inspired appreciation for closures has allowed me to encapsulate common .Net patterns:
Data Access
My usual data access pattern looks something like this:
1 using (IDataReader rdr = db.ExecuteReader(CommandType.Text, sql))
3 {
4 while (rdr.Read())
5 {
6 result.Add( new BusinessObject(...) );
7 }
8 }
9 return result;
Execute the query, loop through the result set, and return a collection of business objects. Rinse. Repeat. Not only does this get repetitive, but I have trouble remembering the using statement. It's not uncommon for me to just forget to dispose the reader when I'm done, which means leaving a database connection open in memory until the garbage collector gets around to cleaning up. Enter GetList:
1 public static List<T> GetList<T>(Database db, string sql, Func<IDataReader, T> create)
2 {
3 List<T> list = new List<T>();
4 using(IDataReader rdr = db.ExecuteReader(CommandType.Text, sql))
5 {
6 while(rdr.Read())
7 {
8 list.Add(create(rdr));
9 }
10 }
11
12 return list;
13 }
GetList
1 //Get the results from the database
2 return DbUtils.GetList<KeyValuePair<int, string>>(db, sql,
3 row => new KeyValuePair<int, string>(
4 DbUtils.AsInt(row.Item["id"]),
5 DbUtils.AsString(row.Item["name"]))
6 );
Where AsInt and AsString are utility methods of mine to handle cases with null fields.
This is, by the way, one example of a very handy use for closures: encapsulating resource allocation and disposal so that you, the developer, don't have to remember to do it. Anyplace you acquire a resource (file access, database access, network operations, etc.) is a candidate for a method like this.
Lazy Loading
Another common pattern of mine is lazy loading: a business object may have some properties which are rarely used and expensive to calculate. In that case, the object defers calculation until they are needed:1 private List<string> names;
2 public List<string> Names
3 {
4 get
5 {
6 if (names != null)
7 {
8 names = ExpensiveComputation();
9 }
10 return names;
11 }
12 }
Using closures, I've extracted this pattern into the library function LazyLoad
1 public static void LazyLoad<T>(ref T stateVar, Func<T> generateVal)
2 {
3 if ( stateVar == null || stateVar.Equals(default(T)) )
4 {
5 stateVar = generateVal();
6 }
7 }
Which transforms the Names property into:
1 private List<string> names;
2 public List<string> Names
3 {
4 get
5 {
6 SysUtils.LazyLoad(ref names,
7 () => ExpensiveComputation()
8 );
9 return names;
10 }
11 }
Notice that even though LazyLoad
The jury is still out for me on whether or not LazyLoad
Sort By
Finally, an example that was someone else's idea, but which was also inspired by Ruby: an extension method that implements SortBy on List1 public static List<TElement> SortBy<TElement, TSortBy>(
2 this List<TElement> coll,
3 Converter<TElement, TSortBy> converter,
4 Comparison<TSortBy> comparison)
5 {
6 return coll
7 .ConvertAll(el => new { Key = converter(el), Value = el })
8 .Sort((a, b) => comparison(a.Key, b.Key))
9 .ConvertAll(x => x.Value);
10 }
11 ...
12 //sample use:
13 List<string> files = ...//Get some files
14 files.SortBy(x => GetDate(x)); //sort by modified date
Notice how type inference cleans up the syntax. This function is as elegant (I think) as the Ruby equivalent, but is still fully type-safe.
No comments:
Post a Comment