检查私有字段与捕获异常

时间:2015-03-26 18:55:04

标签: c# exception

我有一个来自第三方程序集的类(所以我无法编辑它):

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

3 个答案:

答案 0 :(得分:4)

这取决于您希望在应用程序中看到的不变量。

1。如果您希望有很多MyClass具有不同的状态(登录,注销),那么最好避免异常开销(因为异常是< strong>异常情况)并使用一些特定的公共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-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,那么根本不处理异常会更好 - 如果发生了错误,那么它是一些编程错误,因此程序将被终止。

答案 1 :(得分:2)

首先,如果你的类没有公开LoggedIn的至少一个只读属性,那么听起来有一个相当大的设计缺陷。

对于速度,使用反射通常会更快,特别是如果您使用FieldInfo缓存Func<bool>或构建System.Linq.Expressions。这是因为Exceptions在抛出时会收集大量调试信息,包括StackTrace,这可能很昂贵。

与任何事情一样,通常最好对这些操作进行测试,因为有时优化或其他因素可能会让您感到惊讶。

答案 2 :(得分:0)

如果模式if (CanFoo) Foo();非常出现,那么这往往意味着非常强烈:

  1. 正确编写的客户端会知道何时能够或不能调用Foo。客户不知道的事实表明它可能在其他方面存在缺陷。

  2. 公开CanFooFoo的类还应该公开一个方法Foo如果可能且适当(如果无法确定预期的后置条件,该方法应该抛出,但如果在通话之前建立了后置条件,则应该默默返回

  3. 如果第一类不能控制应该提供这样的方法但是没有,那么最干净的方法可能是编写自己的包装方法,其语义反映了缺失方法本应具有的方法。如果该类的更高版本实现了缺少的方法,那么更改一个代码以使用该实现可能比重构大量if (CanFoo)构造更容易。

    顺便说一句,我建议正确设计的类应该允许调用代码来指示它是否期望从登录状态转换到注销状态,或者它是否想要以注销状态结束但是它并不关心它是如何到达那里的。这两种语义都具有完全合法的用途;在第一种适当的情况下,如果在一个封闭的会话上调用,使用LogOut方法抛出异常将是一件好事,但是在客户端代码只想确保它被注销的情况下,可以无条件调用的EnsureLoggedOut方法比为此目的添加额外的客户端代码更清晰。