c# - Debug.Assert() 有意想不到的副作用 - 寻找替代品

标签 c# wpf debugging

我经常使用断言来检测意外的程序状态。我认为断言是一个条件消息框,它会立即停止所有线程,以便(在按下“重试”时)我可以检查当前的应用程序状态。

事实并非如此!当断言消息打开时,我的 wpf 应用程序继续处理事件。这是荒谬的,因为在进入调试器时,情况可能与断言最初“看到”的情况完全不同。您可能会遇到这样的情况,即通过断言本身检查要触发的断言,您可以递归执行方法 - 结果是多个断言或程序永远不会正常出现的状态。

据我对断言函数的理解,这是设计使然的问题。该对话框与应用程序本身在相同的 GUI 线程上运行,因此需要为自己的目的处理消息。但这通常具有所描述的副作用。

所以我正在寻找一个 assert 替代方案,它可以满足在调用时停止所有正在运行的线程的要求。作为解决方法,我有时使用“Debugger.Break();”如果在没有调试器的情况下启动,它(不幸的是)没有效果。

为了说明问题,请看以下代码片段,以最简化的方式产生了一些现象:

public partial class MainWindow : Window
{
  int _count = 0;

  public MainWindow()
  {
    InitializeComponent();
  }    
  private void onLoaded(object sender, RoutedEventArgs e)
  {
    test(); 
  }
  protected override void OnLocationChanged(EventArgs e)
  {
    base.OnLocationChanged(e);
  }    
  void test()
  {
    ++_count;
    Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
    {
      test();
    }));

    Trace.TraceInformation(_count.ToString());
    Debug.Assert(_count != 5);
  }
}

运行代码时,观察开发人员工作室的输出面板。您将看到数字上升到 5,然后断言触发。但是当对话打开时,数字仍在增加。因此,断言打开时断言的条件会发生变化! 现在检查主窗口——它仍然有响应。在“base.OnLocationChanged(e);”处设置一个断点并移动主窗口 => 您将到达断点。但请注意调用堆栈:

MainWindow.OnLocationChanged(System.EventArgs e)    
(…)    
System.dll!Microsoft.Win32.SafeNativeMethods.MessageBox(System.IntPtr 
System.dll!System.Diagnostics.AssertWrapper.ShowMessageBoxAssert(stri
System.dll!System.Diagnostics.DefaultTraceListener.Fail(string message, str 
System.dll!System.Diagnostics.DefaultTraceListener.Fail(string message)
System.dll!System.Diagnostics.TraceInternal.Fail(string message)
System.dll!System.Diagnostics.Debug.Assert(bool condition)
MainWindow.test()
MainWindow.test.AnonymousMethod__0()

这清楚地表明可以在断言打开时执行任意代码。

所以我正在寻找一种类似于断言的机制来停止所有现有线程并在它自己的(线程)上下文中运行。 有什么想法吗?

最佳答案

您正在了解有关调度程序循环工作原理的更多信息。是的,默认跟踪监听器用于报告失败的 MessageBox 对停止您的程序没有多大作用。它旨在阻止用户,它是一个禁用所有用户输入的模态对话框。但不会停止您在代码中所做的任何事情。就像调用 Dispatcher.BeginInvoke() 一样。

您将需要 TraceListener.Fail() 方法的另一个实现。这很有可能,请编辑您的 App.xaml.cs 文件并使其看起来类似于:

using System.Diagnostics;
...
    public partial class App : Application {
        public App() {
            if (Debugger.IsAttached) {
                var def = Debug.Listeners["Default"];
                Debug.Listeners.Remove(def);
                Debug.Listeners.Add(new MyListener(def));
            }
        }

        private class MyListener : TraceListener {
            private TraceListener defListener;
            public MyListener(TraceListener def) { defListener = def; }
            public override void Write(string message) { defListener.Write(message); }
            public override void WriteLine(string message) { defListener.WriteLine(message); }

            public override void Fail(string message, string detailMessage) {
                base.Fail(message, detailMessage);
                Debugger.Break();
            }
        }
    }

该代码通过从已安装的监听器中删除 DefaultTraceListener(让您头疼的那个)来工作。并添加一个自定义的 MyListener 类。这没什么用,只是使用原始监听器来获取显示在“输出”窗口中的消息。但是通过覆盖 Fail() 消息,它会自动触发调试器中断。这里正是您想要的。

关于c# - Debug.Assert() 有意想不到的副作用 - 寻找替代品,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18044707/

相关文章:

c# - 通过单击 gridview 行中的任意位置在 gridview 中触发 CheckedChanged

c# Selenium Chromedriver 打开错误的 url onload

c# - 如何从 C#.net 通过 DeviceIoControl 使用 IOCTL_SCSI_MINIPORT?

c# - 动画 WPF 菜单

c# - TextBlock 中的绑定(bind)在 WPF 中不起作用

php - Intellij 在调试时将项目文件作为非项目文件打开

c# - 创建一个基本 View 模型,但似乎无法在 OnActionExecuting 中 Hook 它

c# - WPf 哪个最适合数据集或 Entity Framework ?

javascript - IE8开发者工具调试器给出错误的异常信息

.net - xslt 调试将所有值显示为 null