2015-03-26 3 views
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», но, учитывая выбор, я лично пойду с тем, чтобы поймать исключение. – juharr

+0

Нам нужен больше контекста, основанный на коде, который я сейчас вижу, что этот подход является ужасным. У вас должна быть лучшая архитектура, чтобы представлять статус входа указанного пользователя. По крайней мере, «Свойство» вы можете получить. – Greg

+0

Попробуйте оба и узнайте сами. Или просто избегайте проблемы и не пытайтесь выходить из системы несколько раз в первую очередь. – Servy

ответ

2

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

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

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

4

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

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

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

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-Bход в систему:

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

Можете ли вы обновить свой ответ, чтобы показать, что этот класс находится в сторонней сборке, и я не могу его обновить? – Arithmomaniac

+2

Вариант один может быть достигнут путем написания класса обертки. – juharr

0

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

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

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

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

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

Смежные вопросы