【问题标题】:Exception handling: how granular would you go when it comes to argument validation?异常处理:当涉及到参数验证时,你会走多远?
【发布时间】:2009-07-09 05:49:12
【问题描述】:

我正在编写一个简单的小类,其中包含一个发送电子邮件的方法。我的目标是在旧版 Visual Basic 6 项目中实现它,通过 COM 互操作工具将其作为 COM 对象公开。

我发现有一个细节很难解决,那就是我在验证参数时应该有多细。从这个角度来看,我真正不满意的一件事,而且根本不是细节,是我实际处理异常的方式:

public class MyMailerClass
{
    #region Creation
    public void SendMail(string from, string subject, string to, string body)
    {
        if (this.IsValidMessage(from, subject, to, body)) // CS1501
        {
            MailMessage msg = new MailMessage();
            msg.IsBodyHtml = true;
            msg.From = new MailAddress(from);
            msg.To.Add(to);
            msg.Subject = subject;
            msg.Body = body;
            SmtpClient srv = new SmtpClient("SOME-SMTP-HOST.COM");
            srv.Send(msg);
        }
        else
        {
            throw new ApplicationException("Invalid message format.");
        }
    }
    #endregion Creation

    #region Validation
    private bool IsValidMessage(string from, string subject, string to, string body)
    {
        Regex chk = new Regex(@"(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})");
        if (!chk.IsMatch(from))
        {
            return false;
        }
        if (!chk.IsMatch(to))
        {
            return false;
        }
        if (!string.IsNullOrEmpty(subject))
        {
            return false;
        }
        if (!string.IsNullOrEmpty(body))
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    #endregion Validation
}

任何建议将不胜感激,因此提前非常感谢您的所有 cmets!

注意:在这种特殊情况下实现 Enterprise Library 的Validation Application Block 是否方便?

【问题讨论】:

  • 作为旁注,您使用 ArgumentException 错误 - 第二个参数是一个 string 应该是参数的 name无效的。相反,您在那里传递参数的值。你应该这样做:throw new ArgumentException("Invalid sender address: " + from, "from");
  • 非常感谢 Pavel,我正在添加它!
  • 新版本的代码将难以调试。在 IsValidMessage() 中,您可以在一行中检查所有条件。当您使用调试器单步执行代码时,您将如何找出哪个不满意?你可以这样写: if( string.IsNullOrEmpty(subject) ) { return false; } if( !string.IsNullOrEmpty(body)) { return false; } 然后创建一个 Regex 对象并再次检查一个条件,一旦条件不满足,立即返回 false。

标签: c# vb6 exception-handling com-interop


【解决方案1】:

考虑一下您对 SendMail 的调用者施加的合同。他们需要向您传递一个“有效的电子邮件地址”。谁决定什么是有效的? SendMail 可以。基本上,您的方法是“高度维护”——它希望事情完全按照自己喜欢的方式进行,而判断您要给它的东西是否令人满意的唯一方法是尝试并希望做到最好。

如果没有让调用者有机会知道如何满足它,或者至少有办法避免异常,就不要编写高维护的方法。将验证逻辑提取到返回布尔值的“IsValidAddress”方法。然后让您的 SendMail 方法调用 IsValidAddress 并在它无效时抛出。

您可以从这个更改中获得几个不错的效果:

(1) 增加关注点分离。 SendMail 的工作是使电子邮件机制工作,而不是判断电子邮件地址是否有效。将该策略决策隔离到专门用于验证的代码中。

(2) 地址验证本身就是一个有用的工具;很多时候,您想知道一个地址是否格式正确,而无需向其发送邮件。

(3) 您可以轻松更新和改进您的验证逻辑,因为它都在一个合理的地方。

(4) 调用者有办法保证不会抛出异常。如果调用者不能在不保证参数有效的情况下调用方法,那么他们必须捕获异常。理想情况下,您永远不应该让调用者必须处理异常以使其代码正确;应该有一种方法可以编写出永远不会抛出的正确代码,即使他们收到的数据是错误的。

这里有几篇我写的关于这个主题的文章,你可能会觉得有帮助:

异常处理:http://ericlippert.com/2008/09/10/vexing-exceptions/

高维护方法:http://blogs.msdn.com/ericlippert/archive/2008/09/08/high-maintenance.aspx

【讨论】:

  • Eric,非常感谢您提供如此详尽而中肯的建议!
【解决方案2】:

连续有两个 throw 语句没有任何意义 - 只会执行第一个语句,然后控制权将传递给异常处理程序,而不会传递给第二个 throw

在我看来,仅仅说“发件人电子邮件无效”之类的就足够了。电子邮件非常简单且简短,因此用户无需任何额外指导即可解决此问题。

我也认为最好先检查所有传入的值,然后才开始工作。如果您随后遇到无效的参数值并抛出异常并且永远无法完成这项工作,那么部分工作的意义何在。尽可能早地指出错误——如果可能的话,从一开始就指出。

【讨论】:

  • 所以你的意思是像检查,也许以三元方式,在方法的一开始就检查每个参数的有效性,有点像携带一个布尔标志,可能是“isMsgOk = true”的形式结束,然后在那里做所有的作曲?
  • 不,为什么在出现异常时使用标志?您可以检查每个参数,一旦发现第一个无效值,就抛出异常。
【解决方案3】:

还有:

使用

string.IsNullOrEmpty(subject)

而不是

subject == null

用于检查您的字符串是否为空。

【讨论】:

  • 感谢sn-p Jason!我包括它。
猜你喜欢
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
  • 1970-01-01
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多