5

DebuggerHidden is pretty handy for marking helper methods, making sure unhandled exceptions stop the debugger in a convenient place:

                  enter image description here

Unfortunately this doesn't seem to work with iterator blocks:

          enter image description here

(if it did, the debugger would show the in as the current statement in the second example).

While this is obviously a limitation of Visual Studio (for which I've submitted a report), is there some way I could perhaps work around this issue while still using an iterator block?

I am guessing that this happens because the compiler-generated code to implement the iterator isn't marked with [DebuggerHidden]. Perhaps there's some way to convince the compiler to do that?

Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • I don't really understand your design decision of hiding exceptions this way. I can see two situations that can occur: 1) You're writing a library to be used by other developers. In this case, they don't have your sources and in VS the lowest visible stack level will be ThrowIterator. So it is what you wanted in the first place. 2) This code is used only by your team, internally, for your project. In this case, why would you let an uncaught exception slip like that and not handle it? In case it's thrown, even when debugging, you actually want to see that it's inside ThrowIterator, and most imp – Marcel N. Jun 15 '12 at 18:55
  • Surely it doesn't matter why I'm using this feature. It's there and I just want to see how to get it to work in all circumstances. – Roman Starkov Jun 15 '12 at 19:50
  • But if you're curious, consider a `PerformQuery` method, which throws if the arguments make no sense. It's only used internally by our team. The exception is uncaught because it's a debug build, which *specifically avoids* catching exceptions *just* so that Visual Studio stops at exactly the right place. If the project just closed with a generic "there was a problem" message, wouldn't that only make debugging unnecessarily harder? We *know* there's a bug, might as well stop at exactly the right line! – Roman Starkov Jun 15 '12 at 19:51
  • Then it's surely a tough one. Not sure if you can tap into the iterator generated code. Most likely not. – Marcel N. Jun 15 '12 at 20:12

1 Answers1

0

Maybe not the answer you hoped for but as a workaround it could gain some points...not sure if you already dreamed this up.

Have a helper class that unwraps your iterator and then use an extension method to bring the wrapper into play on your iterator. I handle exceptions and rethrow. In VS2010 I had to strangely Uncheck the debug option 'Enable just my code' to get behavior close to what OP asked for. Leaving the option checked still drops you in the actual iterator but ik looks like one line too far.

That makes this answer more an experiment to prove and underpin that better compiler support is needed for the scenario to work.

Extension method helper class:

public static class HiddenHelper
{
    public static HiddenEnumerator<T> Hide<T>(this IEnumerable<T> enu )
    {
        return HiddenEnumerator<T>.Enumerable(enu);
    }
}

Wrapper:

public class HiddenEnumerator<T> : IEnumerable<T>, IEnumerator<T>
    {
        IEnumerator<T> _actual;
        private HiddenEnumerator(IEnumerable<T> enu)
        {
            _actual = enu.GetEnumerator();
        }

        public static HiddenEnumerator<T> Enumerable(IEnumerable<T> enu )
        {
            return new HiddenEnumerator<T>(enu);
        }

        public T Current
        {
            [DebuggerHidden]
            get 
            { 
                T someThing = default(T);
                try
                {
                   someThing = _actual.Current;
                }
                catch
                {
                   throw new Exception();
                }
                return someThing; 
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return this;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        public void Dispose()
        {
            _actual.Dispose();
        }

        object IEnumerator.Current
        {
            get { return _actual.Current; }
        }

        [DebuggerHidden]
        public bool MoveNext()
        {
                bool move = false;
                try
                {
                    move = _actual.MoveNext();
                }
                catch
                {
                     throw new IndexOutOfRangeException();
                }
               return move;
        }

        public void Reset()
        {
            _actual.Reset();
        }
   }

Usage:

        public IEnumerable<int> Power(int number, int exponent)
        {
            int counter = 0;
            int result = 1;
            while (counter++ < exponent)
            {
                if (result>Int16.MaxValue) throw new Exception();
                result = result * number;
                yield return result;
            }
        }

        public void UseIt()
        {
            foreach(var i in Power(Int32.MaxValue-1,5).Hide())
            {
                Debug.WriteLine(i);
            }
        }
rene
  • 41,474
  • 78
  • 114
  • 152
  • Doesn't seem to work the way I described in the question :( Also, your "usage" example doesn't actually throw at all, with or without `Hide`. – Roman Starkov Aug 17 '12 at 14:09
  • Yeah, it is capable to pick a stackframe from a piece of code that already executed...trial/error started... – rene Aug 17 '12 at 14:44