如何在调试器暂停时防止其调用某些源代码?

3
在Visual Studio中,当应用程序在调试模式下停止时,您可以将鼠标悬停在对象/属性上以查看其内部内容。

enter image description here

当您像我在上面的图片中所做的那样使用调试器打开对象时,属性将调用其Get方法作为调试器检索属性值以向用户显示的一种方式。
class Program
{
        static void Main(string[] args)
    {
        Foo foo = new Foo();

        foo.MyLock.EnterWriteLock();
        foo.Bar = 5.1;
        foo.MyLock.ExitWriteLock();

        // "I stop here and attempt to see what the value of Bar is via the debugger."
        foo.MyLock.EnterReadLock();
        Console.WriteLine(foo.Bar);
        foo.MyLock.ExitReadLock();
    }
}

class Foo
{
    private double bar;
    public double Bar 
    {
        get
        {
            Console.WriteLine(MyLock.IsReadLockHeld);
            Debug.Assert(MyLock.IsReadLockHeld, "Please enter the read lock before attempting to read this property.");
            return bar;
        }
        set
        {
            Debug.Assert(MyLock.IsWriteLockHeld, "Please enter the write lock before attempting to write this property.");
            bar = value;
        }
    }

    public ReaderWriterLockSlim MyLock { get; set; }

    public Foo()
    {
        MyLock = new ReaderWriterLockSlim();
    }
}

在我的应用程序中,我在我的Get访问器中添加了一个Debug.Assert()调用,以确保在此示例中的Foo已被锁定。每当我的代码调用Bar时,按设计,foo应该被锁定,但是当调试器尝试查看bar时,foo将不会被锁定,这意味着断言应该失败。
当调试器遇到失败的断言时,它有时会抑制断言弹出窗口,而其他时候则会显示与正常失败断言行为相同的断言弹出窗口。据我所知,Assert弹出窗口似乎被抑制了1-2次,但在那些第一次1-2次之后,每次都允许弹出窗口显示。虽然这是断言抑制的最常见行为,但并非总是如此,因为在应用程序的其他运行中,调试器从未停止抑制,无论调试器查看Bar多少次。
问题:对于我的应用程序,期望的行为是100%地抑制断言。我该如何实现这一点?
编辑:此外,如果调试器遇到其中一个断言并且失败,则会将下面的消息写入调试输出。无论是否抑制断言,这都是完全相同的消息。
---- DEBUG ASSERTION FAILED ----
---- Assert Short Message ----
Please enter the read lock before attempting to read this property.
---- Assert Long Message ----

   at TestApp_Debugging.Foo.get_Bar() in c:\Users\Adrian.vanBerkhout\Documents\Visual Studio 2013\Projects\TestApp_Debugging\TestApp_Debugging\Program.cs:line 37
   at TestApp_Debugging.Program.Main(String[] args) in c:\Users\Adrian.vanBerkhout\Documents\Visual Studio 2013\Projects\TestApp_Debugging\TestApp_Debugging\Program.cs:line 17
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

5
DebuggerDisplayAttribute 可以帮助……或在 VS 中禁用属性评估(个人认为在属性 getter 中放置 Assert 时,你得到了你应得的结果 :))。 - Alexei Levenkov
1
我同意@AlexeiLevenkov的观点——做除简单算术外的任何事情的代码应该是一个方法而不是属性。 - Aasmund Eldhuset
@AlexeiLevenkov,你认为我应该像Aasmund建议的那样用方法替换所有属性吗?这肯定可以解决断言问题,但失去属性的功能似乎有些可惜。 - Adrian773
@AlexeiLevenkov 在我的实际应用中,它更加简洁,并且像这样工作:using(foo.AcquireReaderLock()) { DoSomething(foo.Bar) },而不会暴露锁本身。我喜欢完全内部的锁定机制的想法,但是当用户想要执行某种非原子操作时(例如 if (foo.GetBar() > 10) { foo.SetBar(0); }),该怎么办?如果所有的锁定机制都隐藏在 foo 内部,则会创建竞争条件。 - Adrian773
@Adrian773 https://www.bing.com/search?q=c%23+winfrorm+cross+thread - WinForms/WPF 强制使用异常来访问线程,但由于编写代码的自然方式(在 async/await 之前),每个人至少会遇到一次运行错误的线程。如果可以的话,请设计您的 API,以便在最明显的方式下正常使用时,很难出错。 - Alexei Levenkov
显示剩余5条评论
1个回答

4
我在这里找到了一个解决方案(链接)。使用DebuggerBrowsable属性修饰你的问题属性。
[DebuggerBrowsable(DebuggerBrowsableState.Never)]

理想情况下,您永远不需要这样做。但是,我们有一些属性在评估时会触发代码合同异常(在调试中),这会导致Visual Studio非常困惑。您可能需要对类上的属性以及其实现的任何接口进行修饰。


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