Enhancing code readability
I'm a big fan of easily readable code, in fact, my appreciation for code that reads almost English has only grown over the years. Extension methods in C# allow for some nice tricks to take place in this regard.
I first saw the notation "1.Seconds()" in Fun with extension-methods (and similar notations for the DateTime class in DateTime extensions), and have since played around with it myself to be able to write things like "myDateTime.IsBefore(1.Hours().Ago())" instead of mathematically manipulating TimeSpans and DateTimes to death.
This time I'd like to share another C# fun fact that has to do with readability - conversions between method groups and delegates.
Suppose we have the following method:
(One could even go further and have it like so:
I'd say it's a bit overextending, pardon the pun, but that's a matter of personal taste I suppose.)
A trivial usage of GetPeriodicTimer would be:
However, since there is an implicit conversion between method groups and delegates you can let the compiler do the thinking:
Imagine a scenario where you'd like to have two actions invoked whenever the timer elapses. You could, of course, create a separate method and invoke your two actions from within, but that seems a bit off. We'd probably want to write something like:
However, this is the point where the compiler won't play ball and remind you that after all, you're using method groups, not actual delegates, and since the '+' operator is undefined for method groups, things go bad.
In order for the compiler to pick up on what's going on, all we need is to explicitly convert one of the method groups to an action (any of the two will do the trick):
Now the '+' operator has a delegate as the left hand operand and a method group as the right side operand - which it can automatically (and implicitly) convert to an action. Since the '+' operator is well defined for delegates, we're off the hook.
Another benefit is that further actions can be added as method groups as well:
Though if you have more than just a few, it's probably best to extract them to a separate method after all, for the sake of... well, enhancing code readability.
I first saw the notation "1.Seconds()" in Fun with extension-methods (and similar notations for the DateTime class in DateTime extensions), and have since played around with it myself to be able to write things like "myDateTime.IsBefore(1.Hours().Ago())" instead of mathematically manipulating TimeSpans and DateTimes to death.
This time I'd like to share another C# fun fact that has to do with readability - conversions between method groups and delegates.
Suppose we have the following method:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Creates and starts a timer that performs an action upon each time an interval elapses. | |
/// </summary> | |
/// <param name="interval">The interval that will be configured for the created timer.</param> | |
/// <param name="uponTimerElapsed">The action to be performed by the counter.</param> | |
/// <returns>The timer instance created.</returns> | |
public Timer GetPeriodicTimer(TimeSpan interval, Action uponTimerElapsed) | |
{ | |
var timer = new Timer(interval.TotalMilliseconds) { AutoReset = true }; | |
timer.Elapsed += (sender, args) => uponTimerElapsed(); | |
timer.Start(); | |
return timer; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Creates and starts a timer that performs an action upon each time an interval elapses. | |
/// </summary> | |
/// <param name="interval">The interval that will be configured for the created timer.</param> | |
/// <param name="uponTimerElapsed">The action to be performed by the counter.</param> | |
/// <returns>The timer instance created.</returns> | |
public static Timer GetPeriodicTimer(this TimeSpan interval, Action uponTimerElapsed) | |
{ | |
var timer = new Timer(interval.TotalMilliseconds) { AutoReset = true }; | |
timer.Elapsed += (sender, args) => uponTimerElapsed(); | |
timer.Start(); | |
return timer; | |
} | |
// usage: | |
var oneSecondTimer = 1.Seconds().GetPeriodicTimer(...); |
A trivial usage of GetPeriodicTimer would be:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var oneSecondTimer = | |
GetPeriodicTimer( | |
1.Seconds(), | |
new Action(MyActionMethod1)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var oneSecondTimer = | |
GetPeriodicTimer( | |
1.Seconds(), | |
MyActionMethod1); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// wrong, will NOT compile | |
var oneSecondTimer = | |
GetPeriodicTimer( | |
1.Seconds(), | |
MyActionMethod1 + MyActionMethod2); |
In order for the compiler to pick up on what's going on, all we need is to explicitly convert one of the method groups to an action (any of the two will do the trick):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var oneSecondTimer = | |
GetPeriodicTimer( | |
1.Seconds(), | |
new Action(MyActionMethod1) + MyActionMethod2); |
Another benefit is that further actions can be added as method groups as well:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var oneSecondTimer = | |
GetPeriodicTimer( | |
1.Seconds(), | |
new Action(MyActionMethod1) + MyActionMethod2 + MyActionMethod3); |
Comments
Post a Comment