Psychic Debugging, Part One
Here is a compiler bug report I got the other day. The user is trying to write a unit test for a method which takes a Foo and returns a collection of Bars. The test is supposed to confirm that GetBars throws an exception if the argument is null. The test was failing with “got no exception”. The user was wondering if somehow the compiler had optimized away the call.
static void Test() {
bool gotException = false;
try {
IEnumerable<Bar> bars = Foo.GetBars(null);
}
catch (ArgumentNullException ex) {
Console.WriteLine("SUCCESS: Got expected exception");
gotException = true;
}
catch (Exception ex) {
Console.WriteLine("FAILURE: Got unexpected exception");
gotException = true;
}
finally {
if (!gotException)
Console.WriteLine("FAILURE: Got no exception");
}
}
Several people (including myself and Cyrus) psychically debugged this one independently. The user did not include the source code of GetBars, which is what necessitated the use of our psychic powers. Because I am a nice guy, I will give you the beginning:
static public IEnumerable<Bar> GetBars(Foo foo) {
if (foo == null)
throw new ArgumentNullException("foo");
Our psychic powers correctly told us that the behaviour of the test program is correct and expected. What do your psychic powers tell you about the rest of the implementation of GetBars? What is going on here?
Comments
Anonymous
September 05, 2007
My guess would be that GetBars is implemented using one or more "yield return" statements. (The code in the method prior to the first occurrence of "yield return" isn't executed until it is needed, which won't be until the first call to MoveNext.)Anonymous
September 05, 2007
The comment has been removedAnonymous
September 05, 2007
GetBars is an iterator method. Iterators are deferred executed. The Linq Enumerable class actually breaks up iterators into two methods, the first of which validates the call immediately and then calls the second method, which is the actual iterator.Anonymous
September 05, 2007
The comment has been removedAnonymous
September 05, 2007
The comment has been removedAnonymous
September 05, 2007
Yeah, the hint "What do your psychic powers tell you about the rest of the implementation of GetBars?" points me to the yield return solution as well, rather than the operator == one. Mind you, if it WAS yield return, I'm not sure that it's a compiler bug -- that's the expected behaviour, isn't it? Though I must admit that overloading operator == is tricky and has bittem me a couple of times like this too! :-)Anonymous
September 06, 2007
The comment has been removedAnonymous
September 06, 2007
A number of readers have the mysterious fifth sense which gives them the ability to deduce that the GetBarsAnonymous
September 08, 2008
The other day I went to buy some snack from the snack machine in the kitchen. The snack I wanted wasAnonymous
March 31, 2010
On a side note, the test code could be simplified: static void Test() { try { IEnumerable<Bar> bars = Foo.GetBars(null); Console.WriteLine("FAILURE: Got no exception"); } catch (ArgumentNullException ex) { Console.WriteLine("SUCCESS: Got expected exception"); } catch (Exception ex) { Console.WriteLine("FAILURE: Got unexpected exception"); } }Anonymous
April 07, 2010
The comment has been removedAnonymous
May 20, 2011
Cool! Even the <a href="www.authenticpsychicreadings.net">best psychics</a> are not aware of this! Great.