C# – The Beauty of Delegates

So earlier this morning I was at work and had some free time so I decided to work on my game. While working on it I was working on the file operations base class and ran into a situation I didn’t think about when I started it. See in the original code I was using the same logic blocks for six different properties, the only difference between them being the name of the functions that were being called on the .Net framework’s System.IO namespace. It dawned on me that I was using the same twenty or so lines of code over and over and over again and that there should be a way to not do that and move the logic into a function. Then I remembered about delegates, the magical little faries that let you pass functions as parameters in another function. So to make matters short here is the logic code that was being executed before I had this realization, I’ll include two properties just to show how redundant the old code was.

  1. public DateTime CreationTime
  2. {
  3. get
  4. {
  5. // Check to see if the path exists.
  6. if (Exists)
  7. {
  8. // Return the value.
  9. return (IsDirectory) ? SDirectory.GetCreationTime(Path) : SFile.GetCreationTime(Path);
  10. }
  11.  
  12. // Error, return default.
  13. return default(DateTime);
  14. }
  15. set
  16. {
  17. // Check to see if the path exists.
  18. if (Exists)
  19. {
  20. // Check to see if it is a directory.
  21. if (IsDirectory)
  22. {
  23. // It is, invoke the directory method.
  24. SDirectory.SetCreationTime(Path, value);
  25. }
  26. else
  27. {
  28. // It is not, invoke the file method.
  29. SFile.SetCreationTime(Path, value);
  30. }
  31. }
  32. }
  33. }
  34.  
  35. public DateTime CreationTimeUtc
  36. {
  37. get
  38. {
  39. // Check to see if the path exists.
  40. if (Exists)
  41. {
  42. // Return the value.
  43. return (IsDirectory) ? SDirectory.GetCreationTimeUtc(Path) : SFile.GetCreationTimeUtc(Path);
  44. }
  45.  
  46. // Error, return default.
  47. return default(DateTime);
  48. }
  49. set
  50. {
  51. // Check to see if the path exists.
  52. if (Exists)
  53. {
  54. // Check to see if it is a directory.
  55. if (IsDirectory)
  56. {
  57. // It is, invoke the directory method.
  58. SDirectory.SetCreationTimeUtc(Path, value);
  59. }
  60. else
  61. {
  62. // It is not, invoke the file method.
  63. SFile.SetCreationTimeUtc(Path, value);
  64. }
  65. }
  66. }
  67. }
  68.  

As you can see the only four differences between the two properties CreationTimeUtc and CreationTime is that SetCreationTimeUtc and GetCreationTimeUtc are used for the former, and SetCreationTime and GetCreationTime were used for the latter. So I set off on a quest of sorts to reduce the amount redundant code being used and came up with something I am fairly pleased with. The new code, using delegates is as follows.

  1. /// <summary>
  2. /// Defines the delegate method for getting time values of file system objects.
  3. /// </summary>
  4. /// <param name="path">The path of the file system object.</param>
  5. /// <returns>The date time value.</returns>
  6. protected delegate DateTime GetTimeFunction(string path);
  7. /// <summary>
  8. /// Defines the delegate method for Setting time values of file system objects.
  9. /// </summary>
  10. /// <param name="path">The path of the file system object.</param>
  11. /// <param name="value">The new date time value.</param>
  12. protected delegate void SetTimeFunction(string path, DateTime value);
  13.  
  14. /// <summary>
  15. /// Gets the specified date and time of a directory.
  16. /// </summary>
  17. /// <param name="path">The path of the file system object.</param>
  18. /// <param name="directoryFunction">The function to execute if the path is a directory.</param>
  19. /// <param name="fileFunction">The function to execute if the path is a file.</param>
  20. /// <returns>The date time value.</returns>
  21. protected DateTime ExecuteGetTime(string path, GetTimeFunction directoryFunction, GetTimeFunction fileFunction)
  22. {
  23. // Check to see if the path exists.
  24. if (Exists)
  25. {
  26. // Return the value.
  27. return (IsDirectory) ? directoryFunction(Path) : fileFunction(Path);
  28. }
  29.  
  30. // Error, return default.
  31. return default(DateTime);
  32. }
  33.  
  34. /// <summary>
  35. /// Sets the specified date and time for the specified file or directory.
  36. /// </summary>
  37. /// <param name="path">The path of the file system object.</param>
  38. /// <param name="value">The new date time value.</param>
  39. /// <param name="directoryFunction">The function to execute if the path is a directory.</param>
  40. /// <param name="fileFunction">The function to execute if the path is a file.</param>
  41. protected void ExecuteSetTime(string path, DateTime value, SetTimeFunction directoryFunction, SetTimeFunction fileFunction)
  42. {
  43. // Check to see if the path exists.
  44. if (Exists)
  45. {
  46. // Check to see if it is a directory.
  47. if (IsDirectory)
  48. {
  49. // It is, invoke the directory method.
  50. directoryFunction(Path, value);
  51. }
  52. else
  53. {
  54. // It is not, invoke the file method.
  55. fileFunction(Path, value);
  56. }
  57. }
  58. }
  59.  

And the new properties are now much shorter and cleaner looking.

  1. /// <summary>
  2. /// The creation date and time for the specified file or directory.
  3. /// </summary>
  4. public DateTime CreationTime
  5. {
  6. get => ExecuteGetTime(Path, SDirectory.GetCreationTime, SFile.GetCreationTime);
  7. set => ExecuteSetTime(Path, value, SDirectory.SetCreationTime, SFile.SetCreationTime);
  8. }
  9.  
  10. /// <summary>
  11. /// The creation date and time, in Coordinated Universal Time (UTC) format, for the specified file or directory.
  12. /// </summary>
  13. public DateTime CreationTimeUtc
  14. {
  15. get => ExecuteGetTime(Path, SDirectory.GetCreationTimeUtc, SFile.GetCreationTimeUtc);
  16. set => ExecuteSetTime(Path, value, SDirectory.SetCreationTimeUtc, SFile.SetCreationTimeUtc);
  17. }
  18.  

Now putting aside how much I like the look of accessors using the => operator to define values, the new delegate functions actually reduced the code length of the file by about a hundred or so lines, plus as aforementioned, the properties now look a fair bit cleaner too.

Leave Comment