Проверка приватного поля против ловли исключения

1

У меня есть класс из сторонней сборки (поэтому я не могу ее редактировать):

public class MyClass
{
  private bool _loggedIn;
  public void Login() {_loggedIn = true;}
  public void Logout() {
    if (!_loggedIn) throw new InvalidOperationException();
    _loggedIn = false;
   }
}

Теперь предположим, что у меня есть экземпляр MyClass (для которого я не знаю _loggedIn), и мне нужно вызвать LogOut. Какой из следующих способов избежать фатального исключения, как правило, будет быстрее? (любой другой метод тоже будет хорош):

  • Чтобы вызвать LogOut, и если _loggedIn == false, просто поймайте исключение
  • Чтобы использовать отражение, чтобы проверить, что _loggedIn == true, и только вызовите LogOut если это так
  • 0
    Честно говоря, я бы изменил этот класс, чтобы выставить свойство LoggedIn , но если бы у меня был выбор, я бы поймал исключение.
  • 0
    Нам нужно больше контекста, основываясь на коде, который в настоящее время я вижу, этот подход ужасен. Вы должны иметь лучшую архитектуру для представления статуса входа в систему указанного пользователя. По крайней мере, Property вы можете получить доступ.
Показать ещё 3 комментария
Теги:
exception

3 ответа

2
Лучший ответ

Во-первых, если ваш класс не раскрывает, по крайней мере, свойство только для LoggedIn для LoggedIn, это похоже на довольно большой недостаток дизайна.

Для скорости использование отражения будет, как правило, более быстрым, особенно если вы FieldInfo или FieldInfo Func<bool> используя System.Linq.Expressions. Это связано с тем, что Исключения собирают много отладочной информации при броске, включая StackTrace, что может быть дорогостоящим.

Как и в случае с чем-либо, часто лучше всего проверять такие операции, поскольку есть некоторая оптимизация или другие факторы, которые могут вас удивить.

4

Это зависит от инвариантов, которые вы ожидаете увидеть в своем приложении.

1. Если вы ожидаете, что у MyClass будет много разных состояний (вход в систему, выйдите из системы), лучше избегать накладных расходов на исключение (поскольку исключение - это Исключительная ситуация) и использовать какое-то определенное свойство IsLoggedIn (очевидно, чтобы избежать Reflection ) или некоторые TryXxxxx -like.

И даже если вы не можете изменить исходный код, никто не останавливает вас от его упаковки:

public class MyWrappedClass
{
    public Boolean IsLoggedIn {get; private set;}
    private MyClass m_Log;

    public MyWrappedClass ()
    {
        this.m_Log = new MyClass();
        this.IsLoggedIn = false;
    }

    public void Log()
    {
          try
          {
              this.m_Log.LogIn();
              this.IsLoggedIn = true;
          }
          catch
          {
              this.IsLoggedIn = false;
          }
    }

    public void LogOut()
    {
        try
        {
            this.m_Log.LogOut();
            this.IsLoggedIn = false;
        }
        catch
        {
            this.IsLoggedIn = true;
        }
    }
}

Вы даже можете пойти дальше и реализовать с ним IDisposable- интерфейс, чтобы избежать ручного управления LogIn-LogOut:

public class MyWrappedClass
{
    private class LogSessionToken : IDisposable
    {
        private MyWrappedClass parent;
        public LogSessionToken (MyWrappedClass parent)
        {
            parent.LogIn();
        }

        public void Dispose()
        {
            parent.LogOut();
        }
    }

    public IDisposable LogSession()
    {
        return new LogSessionToken (this);
    }
     // ...
}

И используйте его как

using (var logToken = wrappedInstance.LogSession)
{
    // do the work.
} // No need to worry about manual LogOut

2. Если вы планируете использовать только некоторые из MyClass надлежащим образом, тогда было бы лучше не обрабатывать исключение вообще - если что-то случилось, то это некоторая ошибка программирования, поэтому программа должна быть прервана.

  • 0
    Можете ли вы обновить свой ответ, чтобы отразить разъяснение, что этот класс находится в сторонней сборке, и я не могу его обновить?
  • 2
    Первый вариант можно реализовать, написав класс-оболочку.
0

Если шаблон if (CanFoo) Foo(); появляется очень много, что имеет тенденцию очень сильно подразумевать, что либо:

  1. Правильно написанный клиент знал бы, когда он может или не может вызвать Foo. Тот факт, что клиент не знает, указывает на то, что он, вероятно, имеет недостатки другими способами.

  2. Класс, CanFoo и Foo должен также выставлять метод, который будет Foo если это возможно и целесообразно (метод должен бросать, если не удается установить ожидаемые пост-условия, но должен вернуться молча, если пост-условия были установлены до вызова)

В тех случаях, когда класс один не контролирует, должен предоставлять такой метод, но нет, самый чистый подход может заключаться в написании одного собственного метода обертки, семантика которого отражает те недостающие методы, которые должны были иметь. Если более поздняя версия класса реализует отсутствующий метод, изменение одного кода для использования этой реализации может быть проще, чем рефакторинг многих конструкций if (CanFoo).

BTW, я бы предположил, что правильно спроектированный класс должен позволить вызывающему коду указывать, ожидает ли он перехода из состояния входа в систему в состояние выключения или хочет ли он выйти из состояния выхода из системы, но он не " Не волнуйся, как это происходит. Оба вида семантики имеют совершенно законное применение; в случаях, когда первый вид был бы уместным, использование метода LogOut исключение, если вызов на закрытом сеансе был бы хорошим, но в тех случаях, когда клиентский код просто хочет убедиться, что он вышел из системы, имея метод EnsureLoggedOut который может быть вызвано безоговорочно, было бы более чистым, чем добавление дополнительного клиентского кода для этой цели.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню