如何将DebuggerHidden与迭代器块方法结合起来?

5

DebuggerHidden非常方便,可以标记辅助方法,确保未处理的异常在方便的位置停止调试器:

                  enter image description here

很遗憾,这似乎不能与迭代器块一起使用。

          enter image description here

(如果是的话,调试器将在第二个示例中显示in作为当前语句)。
虽然这显然是Visual Studio的限制(我已经提交了报告),但是否有一种方法可以解决这个问题,同时仍然使用迭代器块?
我猜这是因为编译器生成的实现迭代器的代码没有标记为[DebuggerHidden]。也许有一些方法可以说服编译器这样做?

我不太理解你隐藏异常的设计决策。我能看到可能会发生两种情况: 1)你正在编写一个供其他开发人员使用的库。 在这种情况下,他们没有你的源代码,在VS中最低可见的堆栈级别将是ThrowIterator。所以这正是你想要的。 2)此代码仅由您的团队在项目内部使用。 在这种情况下,为什么要让未捕获的异常滑过去而不加处理呢?即使在调试时抛出异常,您实际上也希望看到它位于ThrowIterator内部,这很重要。 - Marcel N.
当然,我使用这个功能的原因并不重要。它就在那里,我只是想看看如何让它在所有情况下都能正常工作。 - Roman Starkov
但如果你好奇的话,可以考虑一个PerformQuery方法,如果参数不合理就会抛出异常。它只在我们团队内部使用。这个异常没有被捕获,因为这是一个调试版本,特别避免捕获异常,只是为了让Visual Studio停在恰当的位置。如果项目只是关闭并显示一个通用的“出现问题”消息,那么这只会使调试变得更加困难。我们知道有一个bug,最好停在恰当的行! - Roman Starkov
那肯定是一个棘手的问题。不确定您是否可以访问迭代器生成的代码。很可能不行。 - Marcel N.
1个回答

0

或许不是你期望的答案,但这个解决方法可以获得一些分数……不确定你是否已经想到了这个方法。

创建一个辅助类来取消包装迭代器,然后使用扩展方法将包装器应用于迭代器。我处理异常并重新抛出。在VS2010中,我必须奇怪地取消调试选项“仅启用我的代码”,以获得接近OP所要求的行为。如果保留选项,则仍会进入实际的迭代器,但看起来多了一行。

这使得此答案更像是一个实验,旨在证明和支持需要更好的编译器支持才能使情境正常工作。

扩展方法辅助类:

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

包装器:

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();
        }
   }

使用方法:

        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);
            }
        }

看起来不像我在问题中描述的那样工作:(另外,您的“用法”示例实际上根本没有抛出异常,无论是否使用Hide - Roman Starkov
是的,它能够从已经执行的代码中选择一个堆栈帧...试错开始了... - rene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接