处理扩展方法中的空值

9

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

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

我想知道在这里处理空值的最优雅方法是什么?在这种情况下是否有典型的遵循模式,例如如果传入空值则返回空值?由于我在此处扩展了字符串类,因此可能希望允许空值并且不抛出参数异常(因为我实际上没有传入参数...)?但是有些人可能会认为我应该像“正常”的方法一样抛出异常。在此您使用的最佳实践是什么?
谢谢!

1
我会像对待普通方法一样对待扩展方法。它只是一种整理代码的方式。你可以将 StringFunctions.ToDigitsOnly(s) 缩短为 s.ToDigitsOnly(),这样如果在其中一个方法中抛出异常,也应该在另一个方法中抛出异常。 - Mikey Mouse
1
如果null是您的应用程序中有效/预期的值,请返回null。否则,抛出异常。个人建议在此处抛出ArgumentException。 - JosephHirn
2
另外一点需要注意的是:在这里你不需要使用正则表达式 String.Join("", input.Where(char.IsDigit)) - I4V
2
你也可以使用 return new string(input.Where(char.IsDigit).ToArray()); - Timothy Shields
4个回答

15

你可以遵循最小惊奇原则:使用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);
}
你可以使用Jon Skeet提出的方法。它将简化您的检查,只需

input.ThrowIfNull("input");

在《深入理解C#》一书中,Jon 在第10.2.4 调用空引用的方法章节有很好的讲解,引用如下:

检查 null 值,作为一名认真负责的开发者,在执行过程中一定会检查参数的有效性。这个扩展方法的奇特特性自然而然地引出了一个问题:当第一个参数是 null(假设它本不应该是 null),你应该抛出哪种异常呢?是 ArgumentNullException,还是 NullReferenceException?我建议使用前者:即使使用了扩展方法语法,它仍然是一个参数。

我认为并且从个人经验来看,遇到空值时最好进行空值检查,特别是对于静态方法,不要依赖于空值。唯一的例外是如果空值正是方法的确切目的,例如ThrowIfNullIsNullOrEmpty扩展方法。


LINQ方法在具有空参数时几乎从不提供任何合理的输出。但这里并非如此,因此这不是一个公平的比较。 - Servy
@Servy 很抱歉,不想争论,但这是为什么呢? myString.Where(char.IsDigit)myString.ToDigitsOnly() 有什么区别? - Ilya Ivanov

1
只要您清楚地传达行为(以便最终用户知道该期望什么),实际上并不重要。 考虑使用内置的 XML文档注释来传达预期行为。
/// <exception cref="ArgumentNullException">argument is null.</exception>
public string Example( string argument )
{
    if ( argument == null )
        throw new ArgumentNullException();
    return argument.ToString();
}

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


1
假设我有这个:
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中,我的想法遭到了反对。标准做法是抛出一个"ArgumentException"异常 - 所以最好还是这样做。

我不建议这样做。你可以使用 AExtensions.F(null) 调用该方法。当传递一个 null 参数时,人们会期望抛出 ArgumentNullException 异常。 - Jim Mischel
1
@JimMischel 确实。我倾向于99%的时间将扩展方法视为它们所应用的类型的成员,尽管像静态方法一样调用它们的情况是不寻常但有效的。使用ArgumentException其实没有什么问题。我想如果.NET这样做的话,那就是正确的方式。 - Timothy Shields
1
抛出NullReferenceException的另一个问题是它并没有真正传达错误源。真正的错误在调用站点,即传递了空参数。如果您的代码抛出NRE(如果您尝试访问它,它会这样做),则客户端会认为错误在扩展方法中。如果客户端使用的是他没有源代码的库,则尤其如此。ArgumentNullException准确告诉客户端问题所在。 - Jim Mischel

1

简单;创建另一个字符串方法,称为IsInValid()

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

在任何需要检查的地方使用...

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接