Если выражение принимает значение false, но все равно веткится, как если бы оно было истинным
Я совершенно тупой. В async-методе у меня есть несколько начальных инструкций-хранителей, которые генерируют исключения, если выполняются особые условия.
Одно из них следующее:
var txPagesCount = _transactionPages.Count;
if (txPagesCount == 0)
throw new InvalidOperationException(string.Format("Cannot commit transaction {0}. It is empty.", _txId));
Предполагается обеспечить наличие страниц в словаре _transactionPages
и выбросить, если их нет.
Это то, что происходит, когда я запускаю его (сборка отладки и отладки, прикрепленный отладчик):
![Number of pages is 3]()
Таким образом, количество страниц в словаре равно 3.
![if statement evaluates to false]()
И так, как и ожидалось, оператор if, сравнивающий 3 к 0, оценивает значение false.
Но тогда, когда шаг дальше:
![Steps into the branch]()
Он вступает в ветвь, как если бы оператор 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));
}
оператор if не веткится, чтобы выбросить исключение. Это относится как к отладке, так и к выпуску. Что-то испортило стек вызовов? Кроме того, если вместо блокировки я добавляю System.Threading.Thread.MemoryBarrier();
после оператора if
, он не войдет в ветвь.
ОБНОВЛЕНИЕ 2
Тайна становится немного больше. Это почти так, как если бы использовались правила С++: 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));
}
Если я прокомментирую "скобки скобок", он войдет в ветвь и выбросит исключение (как в моих исходных изображениях).
FINAL? UPDATE
Хорошо, что отстой. Я сделал несколько изменений в не связанных между собой областях кода, и теперь я больше не могу воспроизвести проблему.
Ответы
Ответ 1
Вау, асинхронная отладка.
Я только нашел один способ сделать это эффективно. Используйте трассировку (надеюсь, что-то приличное, может быть, log4net). Удостоверьтесь, что выводите метку времени и threadId на каждую строку. (также поддерживает синхронизацию вывода на вызов для трассировки библиотеки, когда она вам нужна, которая не должна быть постоянно)
Что это действительно так.
Примечание. Просто запись в файл или stdout работает для первого румянца, но если вы начнете использовать его больше, получите библиотеку.