Thursday, February 02, 2012
Given recent changes in my personal life with my son's diagnosis I will probably be blogging more on a bi-weekly basis instead of weekly for a while. I'm hoping this will be a temporary change until we get things all in order.
Also, I may be sprinkling in a few C++ posts as well to keep things fresh. I've been working on a C++ project as of late and though it is no longer my primary language, I still feel it has a lot of power and is worth knowing well.
Thanks for your understanding!
Thursday, January 26, 2012
C# is a wonderful language for modern programming. While everything in C# has a reason and a place, occasionally there are things that can be confusing for a developer who isn’t aware of what is happening behind the scenes. This is another post in the Little Pitfalls series where I explore these issues; an index of the Little Wonders and Little Pitfalls posts is here.
Many times, we create overloaded methods or constructors to allow them to accept different kinds of data. Further, there are times that we may accept object when any value will do. This works well (aside from boxing/unboxing concerns for value types), but if you have an overload that accepts object and one that takes an enum, and you pass a constant expression of 0, where does it go?
The Pitfall: Constant zero-value expressions are special
Let’s use a contrived example of a messaging framework where you can either construct messages with a specific payload, or construct a “basic” message which will have one of an enumerated set of “standard” payloads. For example, let’s say we allow these basic messages:
1: public enum BasicMessage
2: {
3: Ack,
4: Heartbeat,
5: }
And we allow for a Message to be constructed either using an object, or from a BasicMessage:
1: public class Message
2: {
3: public string Detail { get; private set; }
4:
5: public Message(object messageDetail)
6: {
7: Detail = messageDetail.ToString();
8: }
9:
10: public Message(BasicMessage messageType)
11: {
12: switch (messageType)
13: {
14: case BasicMessage.Ack:
15: Detail = "Acknowledgement";
16: break;
17:
18: case BasicMessage.Heartbeat:
19: Detail = "Heartbeat";
20: break;
21:
22: default:
23: Detail = "Unknown";
24: break;
25: }
26: }
27: }
Seems fine, right? So if we had something like:
1: // Detail will be "This is a test message."
2: var m1 = new Message("This is a test message.");
3:
4: // Detail will be "3.1415927"
5: var m2 = new Message(3.1415927);
6:
7: // Detail will be "Acknowledgement"
8: var m3 = new Message(BasicMessage.Ack);
9:
10: Console.WriteLine(m1.Detail);
11: Console.WriteLine(m2.Detail);
12: Console.WriteLine(m3.Detail);
Everything looks fine and dandy. We see the output:
1: This is a test message
2: 3.1415927
3: Acknowledgement
So it seems that our overloads work and we are able to handle both our special BasicMessage enumeration, and any other object, which will use the ToString() result on that object for the message details. It all sounds straightforward, but what if we had this:
1: // We'd expect Detail to be "0"
2: var m4 = new Message(0);
3:
4: Console.WriteLine(m4.Detail);
We’d expect to get a “0”, but we don’t, we get “Acknowledgement” instead. What happened?
Well, it happens that any (well, nearly any – depending on .NET version) constant zero expression has an implicit conversion to any enum. In short, this means that .NET prefers to implicitly convert the 0 above into BasicMessage instead of an object. This is all according to the language specs which state:
An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any enum-type and to any nullable-type whose underlying type is an enum-type. In the latter case the conversion is evaluated by converting to the underlying enum-type and wrapping the result.
For this to occur, it must be a constant zero expression. That is, it must involve literals or compile-time constants and no variables. In truth, depending on your .NET version, there are some const zero expressions that don’t qualify, but we won’t get too far in the weeds on which do or don’t (See Eric Lippert’s blog posts referenced in the summary for more details). The main point is to be prepared for the possibility of it happening. So, consider the following:
1: // 0 is a constant literal, converts to the enum
2: var m4 = new Message(0);
3:
4: // still a const zero expression, converts to the enum
5: var m5 = new Message(0 * 13);
6:
7: const double IHaveNothing = 0.0;
8:
9: // STILL a const zero value (double at that...), converts to the enum
10: var m6 = new Message(IHaveNothing * 13);
11:
12: // not a const zero expression, because involves a non-const variable, goes to object
13: int x = 0;
14: var m7 = new Message(x);
Curious, eh? Nearly any constant zero value will prefer to implicitly convert to an enum rather than an object if no other choice exists. This is true even if you attempt to force a cast to int, however casting to object would force it to the “correct” side:
1: // The cast to int does nothing here, still const zero expression
2: var m4 = new Message((int)0);
3:
4: // This works, because it will box and then prefer object
5: var m5 = new Message((object)0);
This also means that you can assign an enum a const zero expression as well:
1: // complies, implicit conversion from const zero expression to enum
2: BasicMessage b1 = 0;
3:
4: // does not compile, must cast if non-zero or non-const
5: BasicMessage b2 = 13;
In truth, the situations in which this happens seem to be contrived, yet you’d be surprised how many times this question comes up on sites like Stack Overflow. So how do we keep us from biting us?
The Solution: Make explicit overloads
First of all, this behavior is as designed, this is not a bug, so if this is ever not our desired behavior we should provide overloads for int and the other appropriate numeric value types (double, short, long, etc.) to prevent the issue.
In this way it won’t need to choose between object and an enum, but can directly match the appropriate numeric parameter. Just making an int overload isn’t always enough, this would not catch larger-than-int types like double, long, etc., and it would create an ambiguous overload error for smaller-than-int types like short, byte, etc. since now it can’t decide between the enum or the widening of the value type, as both are simple implicit conversions.
Thus, to really bullet-proof, we either need to have overloads for all the valid numeric value types:
1: public Message(object messageDetail) { ... }
2:
3: public Message(int messageDetail) { ... }
4:
5: public Message(short messageDetail) { ... }
6:
7: public Message(long messageDetail) { ... }
8:
9: public Message(float messageDetail) { ... }
10:
11: public Message(double messageDetail) { ... }
12:
13: // etc for sbyte, byte, char, ushort, uint, ulong, and decimal
Or instead have a method with a generic type parameter. Note that this won’t work on overloading a constructor where the generic type parameter is not a generic type parameter of the class itself (can’t add generic type parameters to a constructor), but it will work on other methods where you may have a similar issue with overloads.
Summary
In short, when you are supplying overloads to handle many different values types, be aware that a constant zero expression can silently and implicitly convert to an enum instead of going to an object.
As such, if this is not the behavior you desire, make sure you have overloads for the primitive numeric types (or use a generic if possible), which also has the added benefit of avoiding boxing costs.
See Eric Lippert’s excellent blog entries “The Root of All Evil, Part One” and “The Root Of All Evil, Part Two” for more details on when and why this happens.
Thursday, January 12, 2012
C# is a wonderful language for modern programming. While everything in C# has a reason and a place, occasionally there are things that can be confusing for a developer who isn’t aware of what is happening behind the scenes. This is another post in the Little Pitfalls series where I explore these issues; an index of the Little Wonders and Little Pitfalls posts is here.
This week will be a short post since I’m travelling to Denver for design meetings, and otherwise spending most of my free time celebrating a birthday with friends and family. But I had a quick item to share that I’ve seen come up more than once on SO, so we’ll add it to the Little Pitfalls.
The System.Diagnostics.Stopwatch class is very useful for elapsed time measurements in .NET applications. Today we’ll look at the ElapsedTicks member of the Stopwatch class, and some of the confusion of what this quantity represents, especially compared to TimeSpan or DateTime Ticks.
The Stopwatch Class
While DateTime is fine for getting the current value of the date and time, it is not exactly precise or lightweight. Thus, when timing a process, a better way to go about this is to use the Stopwatch class from the System.Diagnostics namespace.
The Stopwatch was one of my first “Little Wonders” segments (here). I won’t repeat all of that information here, but as a quick refresher let’s take a look at a small example:
1: // we could also create with new and then call Start(),
2: // but StartNew() does it all in one step
3: var sw = Stopwatch.StartNew();
4:
5: // Do some work here, for now, let's just sleep
6: Thread.Sleep(5000);
7:
8: sw.Stop();
9:
10: Console.WriteLine("We slept for {0} ms", sw.ElapsedMilliseconds);
So, we create and start a Stopwatch instance by calling the factory method StartNew(), then perform the work we want to measure (in this case, just sleeping), and then Stop() our Stopwatch and see how many ElapsedMilliseconds have gone by. The results will vary somewhat (since Thread.Sleep() is not precise), but you would expect something like:
So far, so good! There’s actually a few properties we can use to see how much time has passed between Start() – or StartNew() – and Stop():
- Elapsed – Returns a TimeSpan with the elapsed time.
- ElapsedMilliseconds – Returns a long with the number of elapsed milliseconds.
- ElapsedTicks – Returns a long with the number of Stopwatch elapsed ticks.
The first two are self explanatory, the last needs a bit more explanation as it can be a source of confusion.
Pitfall - Stopwatch Ticks are machine/OS dependent
The thing to remember with ElapsedTicks is that the ticks represented in Stopwatch are based on a combination of the hardware of the machine and the operating system. Contrast this to DateTime and TimeSpan where Ticks are defined as 100 nanosecond intervals -- which is obviously machine/OS independent.
In short, that means that the following output values are (most likely) not going to be identical:
1: var sw = Stopwatch.StartNew();
2:
3: Thread.Sleep(5000);
4:
5: sw.Stop();
6:
7: // This one is the raw ticks of the Stopwatch and is machine dependent
8: Console.WriteLine("ElapsedTicks is: " + sw.ElapsedTicks);
9:
10: // This one goes to a TimeSpan, which normalizes the Ticks
11: // to 100 nanosecond intervals and is machine independent
12: Console.WriteLine("Elapsed.Ticks is: " + sw.Elapsed.Ticks);
For example, if I run this on my current machine, I see the following output:
1: ElapsedTicks are: 9973892461
2: Elapsed.Ticks are: 49993947
Notice in my results that there are a lot more ElapsedTicks than Elapsed.Ticks! And, as I said, if you ran this on a different machine/OS combination, you could get a totally different magnitude of difference. This can be an issue when people think that the ElapsedTicks of the Stopwatch can be directly used to construct a TimeSpan (or DateTime):
1: // Wrong! The ticks are most likely completely different!
2: var elapsedTime = new TimeSpan(sw.ElapsedTicks);
3:
4: Console.WriteLine("Was it {0} or was it {1}",
5: sw.Elapsed, elapsedTime);
So if we tried to create a TimeSpan from ElapsedTicks instead DateTime/TimeSpan ticks, or instead of just using Elapsed (which returns an appropriate TimeSpan), we can get totally different values:
1: Was it 00:00:04.9994341 or was it 00:16:37.3971203
There’s a big difference between four seconds and sixteen minutes! I’ve seen code like the above where the person using Stopwatch didn’t know its ticks were machine/OS dependent and thus made the mistake of using them to construct a TimeSpan instead of simply calling Elapsed to get one.
Sidebar: What’s the Frequency?
For those interested, you can convert from Stopwatch ticks to seconds by using the Stopwatch.Frequency static property, which tells you the ration of Stopwatch ticks per second. Thus these two are (roughly, due to precision differences) the same:
1: // take the ElapsedTicks, then divide by Frequency to get seconds
2: Console.WriteLine("ElapsedTicks to sec: {0}",
3: sw.ElapsedTicks / (double)Stopwatch.Frequency);
4:
5: // take the Elapsed property, and query total number of seconds it represents
6: Console.WriteLine("Elapsed.TotalSeconds: {0}", sw.Elapsed.TotalSeconds);
Which gives us results (on my machine) like:
1: ElapsedTicks to sec: 4.9998583024032
2: Elapsed.TotalSeconds: 4.9998583
Sidebar: Stopwatch is more efficient than DateTime.Now math
You may look at the Stopwatch and think why not just subtract two instances of DateTime.Now, like:
1: var start = DateTime.Now;
2:
3: // Do some work here, for now, let's just sleep
4: Thread.Sleep(5000);
5:
6: var stop = DateTime.Now;
7:
8: Console.WriteLine("The code took: {0} ms", (stop - start).TotalMilliseconds);
There’s two reasons not to do this, though. First of all, the Stopwatch is generally more precise, and secondly, it’s more lightweight.
For example, running the same code using Stopwatch vs DateTime.Now math, the differences in computational speed for 1 million iterations are:
1: DateTime math took: 1534 ms, 0.001534 ms/iteration.
2: Stopwatch took: 70 ms, 7E-05 ms/iteration.
That’s a huge difference between the two! So when you want to measure elapsed time of a process running on a machine, strongly consider the
Stopwatch.
Summary
In short, remember that the ticks in Stopwatch are machine/OS dependent, thus you should never count on the ration of Stopwatch ticks to seconds to be the same between two systems, and possibly even on the same system after a reboot. Thus, you can never count on Stopwatch ticks to be the same interval as DateTime/TimeSpan ticks.
To get system-independent time, make sure to use the Stopwatch’s Elapsed or ElapsedMilliseconds properties, which already take the Stopwatch.Frequency (ticks per second) into account.
Thursday, January 05, 2012
Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders posts can be found here.
A while back I talked about some goodies in DateTime, mostly the properties that let you split out just the date or time. This week, I wanted to look at a couple more methods of the DateTime struct that give you additional control over parsing an input string into a DateTime.
Most of us have dealt with using DateTime.Parse() for these tasks, but sometimes you are wanting to parse something that may not be a valid DateTime, or may be in a non-standard format. So let’s look at the TryParse() and ParseExact() methods that can be used to deal with these two situations.
TryParse() – When your string may not be in a valid format
So, as you probably already know, if you attempt to Parse() an input string that is not in a valid format, or contains invalid values, it will throw a FormatException. For example:
1: // throws FormatException, not a valid format
2: var dt1 = DateTime.Parse("");
3:
4: // throws FormatException, February doesn’t have a 30th day
5: var dt2 = DateTime.Parse("02/30/2010 12:35");
6:
7: // succeeds
8: var dt3 = DateTime.Parse("01/11/1984 13:00");
This is pretty much what you’d expect for many situations, but what if you are processing a file or user input that has a fairly high chance of having an invalid value, what would we do?
Well, obviously, we could just handle the exception and use that to decide how to proceed. For example, if anytime we can’t parse a date we want to assume the current date and time, we could do:
1: string input = "02/30/2010 12:35";
2: DateTime recordDate;
3:
4: // let's say we want to parse the date, but if we can't, then we'll assume now...
5: try
6: {
7: recordDate = DateTime.Parse(input);
8: }
9: catch (Exception)
10: {
11: recordDate = DateTime.Now;
12: }
This works, obviously, but the try/catch block eats up a lot of vertical space, and it incurs the cost of throwing an exception if an error is encountered. When there is a chance that the input is invalid, and you want to handle that invalid situation instead of just bubbling up an exception, consider TryParse() instead.
The TryParse() methods have much the same signature as Parse(), except that they take a final out parameter of DateTime where the value is placed (if successful), and return a bool that indicates if the parse was successful or not. Thus, the code above could be rewritten as:
1: string input = "02/30/2010 12:35";
2: DateTime recordDate;
3:
4: // let's say we want to parse the date, but if we can't, then we'll assume now...
5: if(!DateTime.TryParse(input, out recordDate))
6: {
7: recordDate = DateTime.Now;
8: }
Notice that the code is much more concise without the try/catch, this way, we can attempt the parse, and if all is well the result will be in our recordDate variable, and if not we go into the body of the if (since TryParse() returns false on an error, and we negate the result) and we can then assign a “default” value for recordDate.
It should be noted that Parse() actually calls TryParse() and just throws the exception in the event TryParse() returns false. That is, Parse() is roughly equivalent to:
1: // rough psuedo-code of Parse()
2: public DateTime Parse(string inputString)
3: {
4: DateTime result;
5:
6: if (!DateTime.TryParse(inputString, out result))
7: {
8: throw new FormatException(...);
9: }
10:
11: return result;
12: }
So calling TryParse() directly is more efficient because it avoids the wrapper call, and it doesn’t allocate and throw an unneeded exception in the case of an error.
So let’s time the two methods above on bad data and see what we get over 1,000,000 iterations:
1: TryParse() took: 610 ms, 0.00061 ms/item.
2: Parse() took: 26645 ms, 0.026645 ms/item.
To be fair, these are time differences for 1 million bad items, when you parse good items the times of the two methods perform identically, but if you have a good chance of receiving a badly formatted string and want to directly handle it, then using TryParse() is more efficient.
ParseExact() – When your string is in a non-standard format
What if you were reading data from a file, and the DateTime contained in it was a non-standard format. For example, let’s say we’re parsing a log file that begins with a timestamp that is a date in the format yyyyMMdd HHmmss (like 20111231 031505 for 12/31/2011 03:15:05 AM).
If we attempt to do a DateTime Parse() or TryParse() on this, we will get a failure because it is not one of the standard formats that DateTime’s parsing mechanisms understand.
1: string logString = "20111231 031505";
2: DateTime logEntryTime;
3:
4: try
5: {
6: logEntryTime = DateTime.Parse(logString);
7: }
8: catch (Exception ex)
9: {
10: // the above will throw
11: Console.WriteLine("Didn't understand that DateTime.");
12: }
What we can do in this situation is to call ParseExact() and tell it the exact format we are expecting. We do this by specifying a standard format string or custom format string (much the same as you’d pass to DateTime.ToString() to modify it’s output if you don’t like the default output format).
1: // Note: MM is months, mm is minutes, see MSDN for details
2: logEntryTime = DateTime.ParseExact(logString, "yyyyMMdd HHmmss", null);
3:
4: // outputs: 12/31/2011 3:15:05 AM
5: Console.WriteLine(logEntryTime);
Note that when using the custom format strings, the case and quantity of the format specifiers can matter. For example, “MM” is months and “mm” is minutes, “HH” is 24-hour format and “hh” is 12-hour format, “mm” is zero-padded where “m” is not, etc. For further details see the MSDN.
Also notice that in the snippet above we passed a null for the IFormatProvider. Doing this uses the current culture’s DateTime format provider. If you want to use the invariant culture’s instead you can specify it manually:
1: // these two are identical (current culture)
2: logEntryTime = DateTime.ParseExact(logString, "yyyyMMdd hhmmss", null);
3: logEntryTime = DateTime.ParseExact(logString, "yyyyMMdd hhmmss", DateTimeFormatInfo.CurrentInfo);
4:
5: // this one is invariant
6: logEntryTime = DateTime.ParseExact(logString, "yyyyMMdd hhmmss", DateTimeFormatInfo.InvariantInfo);
So this will help you parse non-standard formats, but in addition to handling invalid formats, ParseExact() is also useful if you want to only accept one format as valid (even if it’s a standard format). This is because you are telling it the explicit format you want to accept, and it doesn’t need to try several formats to see which one works – it only tries the single format specified.
For example, let’s compare doing 1,000,000 iterations of the two pieces of code below:
1: string logString = "12/31/2011";
2: DateTime logEntryTime;
3:
4: // Both work, but ParseExact() wants an explicit format
5: logEntryTime = DateTime.Parse(logString);
6: logEntryTime = DateTime.ParseExact(logString, "MM/dd/yyyy", null);
If we test both of these, we see that ParseExact() is more efficient:
1: Parse() took: 700 ms, 0.0007 ms/item.
2: ParseExact() took: 494 ms, 0.000494 ms/item.
So, if you know the exact format that the date time representation should be, ParseExact() is more efficient. In addition, it will only accept that format, so you can use ParseExact() to narrow the parse behavior to only accept a single format.
Finally, it should be noted that just like Parse(), ParseExact() has a TryParseExact() that returns a bool instead of throwing if the input string is not in the expected format.
Summary
The DateTime struct has a lot of methods for parsing a string into a DateTime. Most everyone has used the Parse() method, but the cost of the exception throws on an error can become a performance bottleneck if improperly formatted input is possible.
Thus, use TryParse() when you want to be able to attempt a parse and handle invalid data immediately (instead of bubbling up the exception), and ParseExact() when the format you are expecting is not a standard format, or when you want to limit to one particular standard format for efficiency.
Thursday, December 22, 2011
The C#/.NET Fundamentals series is geared towards examining fundamental concepts in using C# (and .NET in general) to produce effective solutions.
I wanted to attempt a brief post before the holidays, so I decided to quickly revisit part a post I wrote a few weeks back on The Generic Func Delegates, and in particular, the sidebar on using Func as a generator for unit testing.
At the time, I did not give that short sidebar the attention I really wanted, including showing the setup of the unit tests and discussing the performance impact (if any) of such a practice. So this week I hope to rectify this by revisiting and enhancing the discussion.
Note: Of course, there are many frameworks that also can be used to accomplish these sort of tasks, the main point is to understand the underlying concepts and show more of the power of the Func delegate.
Dependency Injection Beyond Traditional Data Sources
Many people, when they think of Dependency Injection (DI) think of injecting an interface into a constructor or property of a class, so that the interface can be used to mock a traditional data source such as a database or web service. While this is one of the most typical uses of DI, this is not the only type of data we can mock.
For instance, often times we write a class or method and don’t realize that we are tying it to a piece of data that is “moving”. That is, a dependency on logic that varies depending on when or where the test is run.
For example, if we had a piece of code that used DateTime.Now to check for an amount of time to elapse, this would be problematic to unit test because the results of the unit tests could very well vary depending on when the tests are run.
To be sure, this is still data of some form, but many times people write code depending on things like DateTime.Now and then omit unit testing because they think it not easily possible, or instead attempt to write a test harness and vary their system clock, etc. in order to test the logic.
But, there is an easier way! Instead of using these “moving” dependencies directly, we can have a generator create them for us, and be able to inject a generator that produces “non-moving” values for unit testing.
This really is no different than injecting mock data from a data source. In essence the system’s clock is the data source in this example, so it makes sense we could use similar DI principles to test logic depending on it.
An Example Dependency on DateTime.Now
Let’s say you were building your own cache and you wanted each item in the cache to have a DateTime so you could keep track of whether the item has gotten stale (expired) or not. This would be easy enough to write like:
1: // responsible for maintaining an item of type T in the cache
2: public sealed class CacheItem<T>
3: {
4: // time the item was cached
5: public DateTime CachedTime { get; private set; }
6:
7: // the item cached
8: public T Value { get; private set; }
9:
10: // item is expired if older than 30 seconds
11: public bool IsExpired
12: {
13: get { return DateTime.Now - CachedTime > TimeSpan.FromSeconds(30.0); }
14: }
15:
16: // creates the new cached item, setting cached time to "current" time
17: public CacheItem(T value)
18: {
19: Value = value;
20: CachedTime = DateTime.Now;
21: }
22: }
This is just a simplistic example of logic that is strongly dependent on a “moving” target, which makes writing unit tests for this code much more difficult. You could, of course, have unit tests which use something like Thread.Sleep() to wait a given number of seconds and see if the item expires, but this really doesn’t give you much granularity or accuracy at all.
Defining a DateTime Generator
Instead, let’s look at how we can replace DateTime.Now with mock data. So what, in essence is DateTime.Now? It’s a property that returns a DateTime, right? It just happens to be the current DateTime.
Thus we could easily set up a generator to return DateTime.Now and store it in a delegate. Such a method would take no arguments, and return a DateTime thus it’s type can be represented by Func<DateTime>:
1: // define as a lambda that takes nothign and returns DateTime.Now
2: Func<DateTime> nowGenerator = () => DateTime.Now;
3:
4: // The generator gives us current DateTime
5: var now = nowGenerator();
Okay! Now we have a delegate that returns us the current time. If you’re scratching your head wondering why we just performed that cartwheel just to return DateTime.Now, consider this: we can now mock the results of this generator with a specific DateTime:
1: // same delegate, but now mocked to return a particular DateTime
2: nowGenerator = () => new DateTime(2011, 12, 31, 12, 0, 0);
3:
4: // now is now 12/31/2011 12:00:00
5: var now = nowGenerator();
So now with this concept we can unit test our logic with very precise time measurements and see if we get the expected results!
We could either build this generator into the CachedItem<T> class, or we could create a separate utility class for it. For brevity we’ll just build it into our class:
1: // responsible for maintaining an item of type T in the cache
2: public sealed class CacheItem<T>
3: {
4: // generator that returns the current time
5: private static Func<DateTime> _timeGenerator = () => DateTime.Now;
6:
7: // allows internal access to the time generator
8: internal static Func<DateTime> TimeGenerator
9: {
10: set { _timeGenerator = value; }
11: }
12:
13: // time the item was cached
14: public DateTime CachedTime { get; private set; }
15:
16: // the item cached
17: public T Value { get; private set; }
18:
19: // item is expired if older than 30 seconds
20: public bool IsExpired
21: {
22: get { return _timeGenerator() - CachedTime > TimeSpan.FromSeconds(30.0); }
23: }
24:
25: // creates the new cached item, setting cached time to "current" time
26: public CacheItem(T value)
27: {
28: Value = value;
29: CachedTime = _timeGenerator();
30: }
31: }
I gave the generator internal access so that my unit test can change the generator, but to restrict it so it’s not completely open to public. This is just one of many ways to do this, but you get the picture.
If you do make your generator internal and want to also make it visible to your unit test assembly, you’ll need to make your unit test assembly a friend so it has access to internal members. This is done with an assembly level attribute:
1: // make the internals here visible to the friend test assemblies.
2: [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("YourTestAssemblyName")]
Now, we can write unit tests to exercise it!
Unit Testing with the Generator
So, we can start out with a simple test class to verify that the CachedTime property on construction is the “current” DateTime:
1: [TestClass]
2: public class CacheItemTest
3: {
4: // verify on construction it has "current" date and time
5: [TestMethod]
6: public void HasCurrentTimeOnCreation()
7: {
8: var expected = new DateTime(2011, 12, 31, 12, 0, 0);
9: CacheItem<int>.TimeGenerator = () => expected;
10:
11: var target = new CacheItem<int>(13);
12:
13: Assert.AreEqual(expected, target.CachedTime);
14: }
15:
16: // ...
17: }
That passes, so now let’s verify that times under 30 seconds are not expired, so let’s add a few more test methods. First we’ll verify not expired at time of creation (remember generator will return same DateTime on IsExpired check as construction:
1: // verify on construction it's not expired (no time elapsed)
2: [TestMethod]
3: public void NotExpiredOnCreation()
4: {
5: var nowish = new DateTime(2011, 12, 31, 12, 0, 0);
6: CacheItem<int>.TimeGenerator = () => nowish;
7:
8: var target = new CacheItem<int>(13);
9:
10: Assert.IsFalse(target.IsExpired);
11: }
Very good, that passes, so now let’s check at exactly 30 seconds, this should still not be expired since our logic is to expire when greater than 30 seconds:
1: // verify on 30 seconds even it's not expired
2: [TestMethod]
3: public void NotExpiredAtExactlyThirtySeconds()
4: {
5: var expected = new DateTime(2011, 12, 31, 12, 0, 0);
6: CacheItem<int>.TimeGenerator = () => expected;
7:
8: var target = new CacheItem<int>(13);
9:
10: // reset generator to give us the creation time + 30 seconds
11: CacheItem<int>.TimeGenerator = () => expected.AddSeconds(30.0);
12:
13: Assert.IsFalse(target.IsExpired);
14: }
Great! That passes too, now let’s see what happens if we’re even one millisecond over thirty seconds…
1: // verify on 30 seconds even it's not expired
2: [TestMethod]
3: public void ExpiredAboveThirtySeconds()
4: {
5: var expected = new DateTime(2011, 12, 31, 12, 0, 0);
6: CacheItem<int>.TimeGenerator = () => expected;
7:
8: var target = new CacheItem<int>(13);
9:
10: // reset generator to give us the creation time + 30.001 seconds
11: CacheItem<int>.TimeGenerator = () => expected
12: .AddSeconds(30.0).AddMilliseconds(1.0);
13:
14: Assert.IsTrue(target.IsExpired);
15: }
And that passes too! So now we have a solid unit test suite that will exercise this CacheItem<T> and give us the results we are expecting regardless of what time we run the test or how slow our system is!
Performance Implications of Generator Delegate
So, instead of calling DateTime.Now directly in the modified CachedItem<T> code, we are now calling a delegate which returns us DateTime.Now. So how much overhead does that add?
It turns out not much at all, there are cases where the optimizer may be able to inline certain delegate calls, of course, which helps the performance. In particular, when I ran tests of both calling DateTime.Now and calling a delegate that returns DateTime.Now (storing the delegate as coded above, not re-creating the lambda every time) the times over 100 million iterations were nearly identical:
1: Calling DateTime.Now Directly : 75,545 ms (0.000756 ms)
2: Calling DateTime Generator : 75,794 ms (0.000758 ms)
Note that there’s only a difference of 0.000002 ms per call using the delegate. When I repeat this test, I get very similar results every time. The time difference of the generator in all my tests was never higher than 0.000002 ms per call, and during some test runs was actually faster than the direct call to DateTime.Now.
So, even at the highest measurement of 0.000002 ms, this is a very negligible time difference and well worth the tradeoff to achieve better unit test coverage. So unless your application is calling DateTime.Now continuously, you’d most likely never see any overall application time difference at all.
Summary
Among other things, the Func<TResult> can be used to create generators that make it easier to unit tests “moving” data dependencies. Of course, there are other tools you can use (mocking frameworks, Moles, etc) but generators are a very easy and performant solution, and help demonstrate the power of the generic Func delegate family.