【问题标题】:Handling null in extension method在扩展方法中处理 null
【发布时间】:2013-05-16 15:48:53
【问题描述】:

我有一个简单的字符串类扩展方法,它将从字符串中删除所有非数字字符。因此,如果我有一个字符串,例如电话号码,例如“(555) 215-4444”,它会将其转换为“5552154444”。它看起来像这样:

public static string ToDigitsOnly(this string input)
{
    Regex digitsOnly = new Regex(@"[^\d]");
    return digitsOnly.Replace(input, String.Empty);
}

我只是想知道在这里处理空值的最优雅的方法是什么?在这些情况下是否有一个典型的模式可以遵循,例如如果传入 null 则返回 null 值?似乎因为我在这里扩展了字符串类,所以我可能希望允许空值而不抛出争论异常(因为当我使用它时我并没有真正传递争论......)?但是有些人可能会争辩说我应该像“正常”方法一样抛出异常。您在这里使用的最佳做法是什么?

谢谢!

【问题讨论】:

  • 我会将扩展方法视为普通方法。这只是一种整理人们阅读代码的方式。与其将 StringFunctions.ToDigitsOnly(s) 缩短为 s.ToDigitsOnly(),不如将其缩短为 s.ToDigitsOnly(),因此如果您要在其中一个中抛出异常,请在另一个中抛出它。
  • 如果 null 是您的应用程序中的有效/预期值,则返回 null。如果不是,则抛出异常。我个人会在这里抛出一个 ArgumentException。
  • 作为旁注;你在这里不需要正则表达式String.Join("", input.Where(char.IsDigit))
  • 你也可以return new string(input.Where(char.IsDigit).ToArray());

标签: c# extension-methods


【解决方案1】:

您可以遵循最小意外原则:使用 LINQ 中实现的模式:

public static string ToDigitsOnly(this string input)
{
    if(input == null)
          throw new ArgumentNullException("input");

    Regex digitsOnly = new Regex(@"[^\d]");
    return digitsOnly.Replace(input, String.Empty);
}

您可以使用方法proposed by Jon Skeet。它会将您的支票减少到

input.ThrowIfNull("input");

Jon 也有一个很好的部分 10.2.4 Calling a method on a null reference in C# in Depth,引用:

检查 NULL 作为一名尽职尽责的开发人员,我确信您的 生产方法总是先检查其论点的有效性 进行中。这个古怪的问题自然产生的一个问题 扩展方法的特点是第一次抛出什么异常 参数为空(假设它不应该是)。应该是 ArgumentNullException,好像它是一个正常的参数,或者应该 是 NullReferenceException,如果 扩展方法是一个实例方法开始?一世 推荐前者:它仍然是一个论点,即使扩展 方法语法并不那么明显。

我认为这个建议是(根据我的个人经验):检查 null 总是更好,特别是对于静态方法,不要依赖 null 值。只有当它是您的方法的确切用途时才例外,例如ThrowIfNullIsNullOrEmpty 扩展方法。

【讨论】:

  • LINQ 方法实际上从来没有任何输出,当它们有空参数时它们可以合理地提供。这不是这里的情况,所以这不是一个公平的比较。
  • @Servy 抱歉,不想争论,但这是为什么呢? myString.Where(char.IsDigit)myString.ToDigitsOnly() 有什么区别?
【解决方案2】:

只要你能很好地传达行为(以便最终用户知道会发生什么),这并不重要。

考虑使用内置的XML Documentation Comments 来传达预期的行为。

/// <exception cref="ArgumentNullException">argument is null.</exception>
public string Example( string argument )
{
    if ( argument == null )
        throw new ArgumentNullException();
    return argument.ToString();
}

有关许多示例,请参阅 MSDN 文档:

【讨论】:

    【解决方案3】:

    假设我有这个:

    class A
    {
        public void F()
        {
            //do stuff
        }
    }
    

    如果我随后运行以下代码,会发生什么?

    A a = null;
    a.F();
    

    你会得到一个NullReferenceException。所以我会说编写等效扩展方法的正确方法如下。

    class A
    {
    }
    
    static class AExtensions
    {
        void F(this A a)
        {
            if (a == null)
            {
                throw new NullReferenceException();
            }
            //do stuff
        }
    }
    

    然而,.NET 不同意我的观点。 .NET 中的标准是改为抛出 ArgumentException - 所以最好改为这样做。

    【讨论】:

    • 我不建议这样做。您可以使用AExtensions.F(null) 调用该方法。当null 参数被传递时,人们会期望ArgumentNullException
    • @JimMischel 真的。不过,我倾向于 99% 的时间将扩展方法视为它们所应用的类型的成员。像静态方法一样调用它们的情况虽然有效,但并不常见。不过,使用 ArgumentException 确实没有任何问题。我想如果 .NET 正在这样做,那就是要走的路。
    • 抛出NullReferenceException 的另一个问题是它并不能真正传达错误的来源。真正的错误是在调用站点,其中传递了一个空参数。如果您的代码抛出 NRE(如果您只是尝试访问它,它无论如何都会这样做),客户端会被引导相信错误出在扩展方法中。如果客户正在使用他没有源的库,则尤其如此。 ArgumentNullException 告诉客户确切地问题是什么。
    【解决方案4】:

    简单;为 String 创建另一个方法,比如 IsInValid()

    public static bool IsInValid(this string s) { 返回(s == null)|| (s.Length == 0); }

    使用任何你想检查的地方......

    此外,您可以在任何地方使用此扩展程序

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多