Posts
203
Comments
1116
Trackbacks
51
February 2009 Entries
Custom C# 3.0 LINQ Max Extension Method

The System.Core assembly in .NET 3.5 contains the main LINQ methods for dealing with objects such as the Max() extension method. Like many of the LINQ extension methods, the Max() method has many overloads that allow you to do things like this:

   1:  List<int> list = new List<int> { 1, 2, 17, 14, 21, 4 };
   2:  Console.WriteLine("Max: " + list.Max()); //<- "Max: 21"

This is all well and good but what if you need to do something a little more interesting?  There are endless examples to think of but for the sake of this discussion, let’s say we have a directory and we want to find the latest/newest file in that directory.  This isn’t very complicated and there are several ways to do it but one simple example might be this:

   1:  private string GetNewestFileInDirectory(string directory)
   2:  {
   3:      FileInfo latestFile = null;
   4:      foreach (var fileName in Directory.GetFiles(directory))
   5:      {
   6:          FileInfo currentFile = new FileInfo(fileName);
   7:          if (latestFile == null || currentFile.LastWriteTimeUtc > latestFile.LastWriteTimeUtc)
   8:          {
   9:              latestFile = currentFile;
  10:          }
  11:      }
  12:      return latestFile.Name;
  13:  }

For each file in the directory, we’re comparing the last write time and, if it’s greater than any other file timestamp, we store it in the temporary latestFile variable which will eventually be returned.  But wouldn’t it be nicer to be able to use some sort of Max() method in this scenario where we’re considering that the “max” is based on the file’s timestamp?  The FileInfo object doesn’t support any type of IComparable interface so that’s no help – and even if it did, it wouldn’t be much help because there’s no clear idea what it would be based on (e.g., file size? file name? file date?).

Let’s first see what we can do with the OOB Max() extension method. We could do something like this:

   1:  IEnumerable<FileInfo> fileList = Directory.GetFiles(directory).Select(f => new FileInfo(f));
   2:  var result = fileList.Max(f => f.LastAccessTimeUtc);
   3:  Console.WriteLine("Result is: " + result); //<- "Result is: Result is: 2/6/2009 8:10:54 PM"

Notice on line 1 I’m creating an IEnumerable<FileInfo> in a single line of code by leveraging the Select() extension method. The Directory.GetFiles() method just returns an array of strings, but I need a collection of the actual FileInfo objects so i can get at the file properties.  Being able to do this on 1 line of code is much more succinct than having to instantiate and object, loop over the source, and continually call the Add() method of the collection.

Line 2 gives of the Max date which is, in fact, the latest date that we’re looking for.  However, the problem is that I am trying to get the *actual* file that is the latest – just knowing the latest date by itself doesn’t do me a whole lot of good. What I really want to be able to do is the have a Max() method that will determine a “max” of any arbitrary object based on a simple expression that I can specify on-demand. In other words, I want to be able to write the code above but have the result be of type FileInfo so that I can get a reference to the actual FileInfo object that happens to have the maximum date in my collection. This can be done by writing your own customized extension method in roughly a dozen lines of code like this:

   1:  public static T Max<T, TCompare>(this IEnumerable<T> collection, Func<T, TCompare> func) where TCompare : IComparable<TCompare>
   2:  {
   3:      T maxItem = default(T);
   4:      TCompare maxValue = default(TCompare);
   5:      foreach (var item in collection)
   6:      {
   7:          TCompare temp = func(item);
   8:          if (maxItem == null || temp.CompareTo(maxValue) > 0)
   9:          {
  10:              maxValue = temp;
  11:              maxItem = item;
  12:          }
  13:      }
  14:      return maxItem;
  15:  }

This extension method has 2 generic type arguments. The first – T – in this case will be the FileInfo object. The second – TCompare – in this case will be the DateTime representing the result of the LastAccessTimeUtc property. In order to make this work correctly, you must have the “where TCompare : IComparable<TCompare>” generic constraint to ensure that whatever value specified implements IComparable. The rest of the algorithm is pretty similar conceptually to the original code.

Now you have a Max() extension method that can be generalized to limitless scenarios. Do you want to find the file with maximum size? Maximum name (alphabetically)? Maximum creation timestamp? Instead of a FileInfo object, you could use it against a collection of Person objects to find the person with the max age or max last name or max date of birth or max date hired, etc., etc. You could also do the same thing for other extension methods (e.g., Min(), etc.).

Posted On Friday, February 6, 2009 9:48 PM | Comments (11)
C# 4.0 Optional Parameters

One of the new language features coming in C# 4.0 is Optional Parameters. In large part, this is due to Microsoft’s plan of co-evolution with C# and VB.NET since VB.NET has had this feature for a while.  Personally, I’m still somewhat undecided about this feature and there has been much written about method overloads versus optional parameters. For example, Considerations in Overloading Procedures.  Ultimately, I do think there will be some scenarios where this will be useful to make code more concise.

Consider a standard scenario with method overloading or constructor chaining.  In C# we’d have several methods with different signatures where, in effect, we’re really just after default values. Let’s take the scenario where we’ve got a little helper class to send emails in our application. In some cases we want to CC the administrator to troubleshoot issues; in some cases we want rich HTML emails rather than plain text. We might set up our methods like this:

   1:  public void SendMail(string toAddress, string bodyText)
   2:  {
   3:      this.SendMail(toAddress, bodyText, true);
   4:  }
   5:   
   6:  public void SendMail(string toAddress, string bodyText, bool ccAdministrator)
   7:  {
   8:      this.SendMail(toAddress, bodyText, ccAdministrator, false);
   9:  }
  10:   
  11:  public void SendMail(string toAddress, string bodyText, bool ccAdministrator, bool isBodyHtml)
  12:  {
  13:      // Full implementation here
  14:  }

This is pretty standard method overloading and we essentially are setting default values (true for CC the Admin, and false for HTML emails). With C# 4.0 we can now make the code more concise by only having to implement 1 method:

   1:  public void SendMail(string toAddress, string bodyText, bool ccAdministrator = true, bool isBodyHtml = false)
   2:  {
   3:      // Full implementation here
   4:  }

However, you do have to take into account your scenario.  If you have a situation where you actually need to know if the consuming code provided a value then this isn’t a good option because if “true” comes in for the 3rd parameter, you don’t know if the consuming code actually set this explicitly or if it was simply the result of the default value.  But in typical scenarios like this, it’s not a big deal.  Cracking open Reflector and looking at the IL that the C# compiler is generating:

   1:  .method public hidebysig instance void SendMail(string toAddress, string bodyText, [opt] bool ccAdministrator, [opt] bool isBodyHtml) cil managed
   2:  {
   3:      .param [3] = bool(true)
   4:      .param [4] = bool(false)
   5:      .maxstack 8
   6:      L_0000: nop 
   7:      L_0001: ret 
   8:  }

Which Reflectors translates to a C# equivalent of:

   1:  public void SendMail(string toAddress, string bodyText, [Optional, DefaultParameterValue(true)] bool ccAdministrator, [Optional, DefaultParameterValue(false)] bool isBodyHtml)
   2:  {
   3:  }

So if consuming code is written using the least specified “overload” like this:

   1:  email.SendMail("bob@foo.com", "Hello World");

The IL that the C# compiler will generate will actually be the equivalent of this:

   1:  email.SendMail("bob@foo.com", "Hello World", true, false);

What’s more interesting is that, unlike traditional method overloading, you have the ability to omit only the 3rd parameter in conjunction with the new Named Parameters language feature and write your code like this:

   1:  email.SendMail("bob@foo.com", "Hello World", isBodyHtml: true);

This will allow consuming code to only pass 3 arguments for succinctness but still invoke the appropriate overload since the IL generated in that instance will be equivalent to this:

   1:  email.SendMail("bob@foo.com", "Hello World", true, true);

Overall, it’s by no means an earth shattering feature that is being added to the language in stand-alone scenarios (though it will be have a *much* bigger impact in COM Interop).  Used in the correct scenarios, it can improve your code.

Posted On Thursday, February 5, 2009 10:41 PM | Comments (17)
Verbal Graffiti

These days it is absolutely essential to have solid communication and presentation skills as a developer. This will serve you well whether you are presenting at conferences and users groups or professionally on a day-to-day basis communicating with managers and colleagues. I’ve given many presentations at developer user groups and code camps and I’m always looking for little ways to improve my presentations. Recently I picked up a copy of The Exceptional Presenter by Timothy Koegel and it was a great book and a fast read. One of the sections in the book is entitled “Eliminate Verbal Graffiti” where Koegel discusses the importance of eliminating fillers such as “um”, “like”, “you know”, “I mean”, “so”, “uh”, etc. Among his recommendations for eliminating these are to pay attention to other people’s fillers, practice eliminating fillers during every day conversation, and to record yourself during a presentation and listen to the play back of yourself (yikes!).

Recently I recorded a developer screen cast on C# 3.0 so I figured I would use the opportunity to listen to myself in an attempt to identify (and eliminate) my own personal verbal graffiti.  While I was dreading the thought of having to listen to my own voice recorded, I figured it wouldn’t be too bad because I’m generally *ok* with not using “um” all over the place. OH, the misery! The only good news in the ordeal was that yes, in fact, I did a decent job not using “um” a ton of times.  Unfortunately, the rest didn’t go as smoothly.  Within the first 2 minutes I heard myself use the word “so” multiple times. Once you’re really paying attention to yourself, it can become a little brutal.

The most helpful suggestion for deadening the pain came from my wife who suggested inventing the “so” drinking game.  My wife: “Just take a drink every time you hear yourself say that word “so” – it will be fun!”  Hmm, sounds to me like there wouldn’t be too fine a line between “fun” and inappropriately fast inebriation.  So she moved to her next suggestion: “OK, next time you give a presentation at a user group, I’ll come and sit in the audience. You can wear the electric dog collar and every time I hear you say “so” I’ll give you an electric shock so that you can eliminate this from your lexicon.”  Hmm, again.  Some nagging feeling in the back of my mind is telling me my wife might take pleasure in “helping” me get over my issue just a little too much.

I think I’ll stick to the suggestions that Koegel puts forth in his book – I highly recommend this book to anyone looking to improve their presentation skills.

Posted On Tuesday, February 3, 2009 11:39 PM | Comments (4)

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud