我经常使用断言来检测意外的程序状态。我认为断言是一个条件消息框,它立即停止所有线程,以便(按“重试”时)我可以检查当前应用程序状态。
不是这种情况!当断言消息打开时,我的wpf应用程序继续处理事件。这很荒谬,因为在进入调试器时,情况可能与断言最初“看到”的情况完全不同。您可能会遇到这样的情况:对要触发的断言的检查会通过断言本身进行更改,您可以递归执行方法-导致多个断言或状态,程序永远无法正常运行。
据我了解断言功能,这是设计上的问题。对话框与应用程序本身在同一GUI线程上运行,因此需要出于自身目的处理消息。但这通常具有所描述的副作用。
因此,我正在寻找一种断言替代方案,该替代方案应满足在调用时停止所有正在运行的线程的要求。解决方法是,有时使用“ 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);
}
}
Run Code Online (Sandbox Code Playgroud)
在运行代码时,请观看developer Studio的输出面板。您将看到数字上升到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()
Run Code Online (Sandbox Code Playgroud)
这清楚地表明断言打开时可以执行任意代码。
因此,我正在寻找一种像断言之类的机制,该机制将停止所有现有线程并在其自己的(线程)上下文中运行。有任何想法吗?
您将了解有关调度程序循环如何工作的更多信息。是的,默认跟踪侦听器用来报告故障的 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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
该代码的工作原理是从已安装的侦听器中删除令您头疼的 DefaultTraceListener。并添加一个自定义类 MyListener 类。它没有做太多事情,只是使用原始侦听器来获取在“输出”窗口中显示的消息。但通过覆盖 Fail() 消息,它会自动触发调试器中断。这里正是您想要的。