Psychic Debugging, Part One

Psychic Debugging, Part One

  • Comments 12

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?

  • 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.)

  • Something static in Foo is triggering an exception that is not caught by second catch.

  • 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.

  • Foo could have a custom implementation of operator== which returns false when the right operand is null, so the (foo == null) condition doesn't hit.

  • Same as keviowen and Wesner Moise the Iterator part in the C# 2.0 norm say :

    """invoking the function member does not immediately execute the code in the iterator block."""

    So including a yield return in the body of Foo.GetBars will create this result.

    I think that overloading operator== could also be an answer but with an IEnumerable<T> result sound a lot like an iterator :D

  • 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! :-)

  • Or, perhaps, there is an overload Foo(object)?

  • A number of readers have the mysterious fifth sense which gives them the ability to deduce that the GetBars

  • The other day I went to buy some snack from the snack machine in the kitchen. The snack I wanted was

  • 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");

           }

       }

  • @Neil, well, if it is a unit-test, it should be simplified to

    void Test() {

       Assert.Throws<ArgumentNullException >(() => Foo.GetBars(null));

    }

  • Cool! Even the <a href="www.authenticpsychicreadings.net">best psychics</a> are not aware of this! Great.

Page 1 of 1 (12 items)