如果语句的计算结果为false,但仍然分支,就好像它是真的一样

Ale*_*lex 18 c# debugging exception-handling exception

我很难过.在异步方法中,我有一些初始保护语句,如果满足特定条件则抛出异常.

其中之一是以下内容:

var txPagesCount = _transactionPages.Count;
if (txPagesCount == 0)
    throw new InvalidOperationException(string.Format("Cannot commit transaction {0}. It is empty.", _txId));
Run Code Online (Sandbox Code Playgroud)

这应该确保_transactionPages字典中有页面,如果没有则抛出.

这是我运行时发生的事情(发布和调试版本,附加调试器):

页数为3

所以字典中的页数是3.

if语句的计算结果为false

因此,正如预期的那样,将3比0的if语句评估为false.

但是,当进一步迈进时:

进入分支的步骤

它进入分支,好像if语句计算为true,并抛出异常.

我在这里错过了什么?

UPDATE

当我这样做:

private static readonly object _globalLock = new object();

public async Task<Checkpoint> CommitAsync(PageNumber dataRoot, PageNumber firstUnusedPage)
{
    lock (_globalLock)
    {
        if (IsCompleted)
            throw new InvalidOperationException(string.Format("Cannot commit completed transaction {0}", _txId));
        var txPagesCount = _transactionPages.Count;
        if (txPagesCount == 0)
            throw new InvalidOperationException(string.Format("Cannot commit transaction {0}. It is empty.", _txId));
    }
Run Code Online (Sandbox Code Playgroud)

if语句不分支以抛出异常.调试和发布版本都是这种情况.是什么搞砸了调用堆栈?另外,如果不是我System.Threading.Thread.MemoryBarrier();if语句后添加的锁,它将不会进入分支.

更新2

这个谜团变得更大了.这几乎就像使用了c ++范围规则:D下面的代码(在调试版本中)将显示预期的行为:不进入分支而不抛出.在发布版本中,它将进入分支并像以前一样抛出.

private static readonly object _globalLock = new object();

public async Task<Checkpoint> CommitAsync(PageNumber dataRoot, PageNumber firstUnusedPage)
{
    //lock (_globalLock)
    {
        if (IsCompleted)
            throw new InvalidOperationException(string.Format("Cannot commit completed transaction {0}", _txId));
        var txPagesCount = _transactionPages.Count;
        if (txPagesCount == 0)
            throw new InvalidOperationException(string.Format("Cannot commit transaction {0}. It is empty.", _txId));
    }
Run Code Online (Sandbox Code Playgroud)

如果我注释掉"范围括号",它将进入分支并抛出异常(如我原始图像中所示).

最后?UPDATE

那很糟糕.我对不相关的代码区域做了一些更改,现在我不再能够重现问题了.

小智 2

哇,异步调试。

我只找到一种方法可以有效地做到这一点。使用跟踪(希望是像样的东西,也许是 log4net)。确保在每一行上输出时间戳和 threadId。(还支持每次调用跟踪库时的输出同步,以便在需要时同步,但不必始终处于打开状态)

确实如此。

注意:乍一看,简单地写入文件或标准输出是可行的,但如果您开始更多地使用它,请获取一个库。