扩展String类与IsNullOrEmpty混淆?

每个人都知道并喜欢String.IsNullOrEmpty(yourString)方法。

我想知道如果我们将String类扩展为具有这样的方法,是否会混淆开发人员或使代码更好:

yourString.IsNullOrEmpty(); 

优点:

  1. 更具可读性。
  2. 减少打字。

缺点:

  1. 可能会令人困惑,因为yourString变量可以为null ,看起来你正在对null变量执行方法。

你怎么看?

我们可以询问有关myObject.IsNull()方法的相同问题。

那怎么写我:

 public static class StringExt { public static bool IsNullOrEmpty(this string text) { return string.IsNullOrEmpty(text); } public static bool IsNull(this object obj) { return obj == null; } } 

我个人不喜欢这样做。 现在扩展方法的最大问题是可发现性。 你不知道你知道特定类型中存在的所有方法,不可能查看方法调用并知道它是一个扩展方法。 因此,我发现使用普通方法调用无法实现的扩展方法做任何事情都是有问题的。 否则你最终会让开发人员感到困惑。

C ++中存在这个问题的必然结果。 在几个C ++实现中,只要不触摸类型上的任何字段或虚方法,就可以在NULL指针上调用实例方法。 我已经使用了几段有意执行此操作的代码,并在“ this==NULL ”时为方法提供了不同的行为。 与之合作非常令人发狂。

这并不是说我不喜欢扩展方法。 恰恰相反,我喜欢它们并经常使用它们。 但我认为编写它们时应遵循两条重要规则。

  • 将实际的方法实现视为另一种静态方法,因为它实际上是。 例如,对于null,抛出ArgumentException而不是NullReferenceexception
  • 不要让扩展方法执行普通实例方法无法执行的操作

编辑回应Mehrdad的评论

利用它的问题是我没有看到str.IsNullOrEmpty比String.IsNullOrEmpty(str)具有显着的function优势。 我看到的唯一优势是,一个人需要的打字少于另一个。 关于扩展方法,通常也可以这样说。 但在这种情况下,你还会改变人们对程序流程的看法。

如果更短的打字是人们真正想要的,那么IsNullOrEmpty(str)是不是更好的选择? 这既是明确的,也是最短的。 True C#今天不支持这样的function。 但想象一下,如果在C#我可以说

 using SomeNamespace.SomeStaticClass; 

这样做的结果是SomeStaticClass上的所有方法现在都在全局命名空间中并且可用于绑定。 这似乎是人们想要的,但他们将它附加到一个我不是很喜欢的扩展方法。

如果我没有弄错的话,这里的每个答案都谴责扩展方法可以在null实例上调用的事实,因此他们不支持相信这是一个好主意。

让我反驳他们的论点。

我不相信AT all在一个可能为null的对象上调用方法会令人困惑。 事实是我们只检查某些位置的空值,而不是100%的时间。 这意味着我们进行的每个方法调用都有可能在null对象上的时间百分比。 这是可以理解和接受的。 如果不是,我们将在每次调用方法之前检查null。

那么,如何在null对象上发生特定的方法调用会让人感到困惑呢? 看下面的代码:

 var bar = foo.DoSomethingResultingInBar(); Console.Writeline(bar.ToStringOr("[null]")); 

你困惑吗? 你应该。 因为这里是foo的实现:

 public Bar DoSomethingResultingInBar() { return null; //LOL SUCKER! } 

看到? 您可以阅读代码示例而不会感到困惑。 您了解到,foo可能会从该方法调用返回null,而bar上的ToStringOr调用将导致NRE。 你的头旋转了吗? 当然不是。 据了解,这可能发生。 现在,ToStringOr方法并不熟悉。 你在这些情况下做了什么? 您可以阅读该方法的文档或检查调用的代码。 这里是:

 public static class BarExtensions { public static string ToStringOr(this bar, string whenNull) { return bar == null ? whenNull ?? "[null]" : bar.ToString(); } } 

混乱? 当然不是。 很明显,开发人员需要一种简写方法来检查bar是否为null并用非空字符串替换它。 这样做可以显着减少代码,提高可读性和代码重用率。 当然你可以用其他方式做到这一点,但这种方式不会比其他方式更令人困惑。 例如:

 var bar = foo.DoSomethingResultingInBar(); Console.Writeline(ToStringOr(bar, "[null]")); 

当您遇到此代码时,您有什么不同于原始版本? 您仍然需要检查代码,当bar为null时,您仍然必须确定其行为。 你还是要处理这种可能性。

扩展方法是否令人困惑? 只有你不理解它们。 而且,坦率地说,从代表到lambdas,语言的任何部分都可以这样说。

我认为这个问题的根源是Jon Skeet 在他最喜欢的语言 (C#)中讨厌的事情列表:C#不应该自动导入整个命名空间中的所有扩展方法。 应该更明确地完成这个过程。

我个人对这个具体问题的看法是(因为我们无法对上述事实做任何事情)如果你愿意,可以使用扩展方法。 我并不是说它不会让人感到困惑,但是关于扩展方法(可以在null引用上调用)的这个事实是一个全局的事情,并不仅仅影响String.IsNullOrEmpty ,所以C#开发人员应该熟悉它。

顺便说一句,幸运的是,Visual Studio通过IntelliSense工具提示中的(extension)清楚地识别扩展方法。

我非常喜欢这种方法,因为该方法清楚地表明它正在检查对象是否为空。 ThrowIfNull,IsNull,IsNullOrEmpty等。它非常易读。

就个人而言,我不会创建一个扩展来执行已经存在于框架中的内容,除非它是可用性的重大改进。 在这种情况下,我不认为是这种情况。 此外,如果我要创建一个扩展,我会以减少混淆的方式命名,而不是增加它。 我再次认为这个案例未通过该测试。

说了这么多,我确实有一个字符串扩展来测试,不仅是字符串是null还是空,而且还有它只包含空格。 我称之为IsNothing 。 你可以在这里找到它。

它不仅仅看起来像是在null变量上调用方法。 你/是/调用null变量的方法,虽然是通过静态扩展方法实现的。 我不知道扩展方法(我已经很了解)支持它。 这甚至允许你做疯狂的事情,如:

 public static int PowerLength(this string obj) { return obj == null ? 0 : obj.Length; } 

从我现在所处的位置开始,我将在被认为有害的空引用上对任何扩展方法的使用进行分类。

让我们来看看pro:s和con:s ……

  1. 更具可读性。

是的,稍微,但可读性的提高远远超过了这样一个事实:它看起来像是在一个不必是实例的东西上调用实例方法。 实际上它更容易阅读,但它更难理解,因此提高可读性实际上只是一种幻觉。

  1. 减少打字。

是的,但这确实不是一个有力的论据。 如果键入是编程的主要部分,那么您就不会做一些具有足够远程挑战性的东西,以便您作为开发人员进化。

  1. 可能会令人困惑,因为yourString变量可以为null,看起来你正在对null变量执行方法。

是的,(如上所述)。

是的,它会混淆。

我认为每个知道IsNullOrEmpty()认为它的使用非常自然。 因此,您建议的扩展方法不会增加可读性。

也许对于.NET新手来说,这种扩展方法可能更容易处理,但是有可能他/她不理解扩展方法的所有方面(比如你需要导入命名空间,它可能是在null上调用)。 她/他可能想知道为什么这个扩展方法在另一个项目中不存在。 无论如何:即使有人不IsNullOrEmpty() .NET, IsNullOrEmpty()方法的语法可能会很快变得自然。 此外,扩展方法的好处不会超过它引起的混乱。

编辑:试着改写我想说的话。

我认为使用“IsNull”类型调用扩展任何对象有点令人困惑,如果可能应该避免。

有争议的是,IsEmptyString方法可能对String类型有用,并且因为你通常将它与对null的测试结合起来,IsNullOrEmpty可能是有用的,但是由于字符串类型的事实我也避免这样做已经有一个静态的方法,这样做,我不确定你是在节省多少打字(最多5个字符)。

在null变量上调用方法通常会导致NullReferenceException。 IsNullOrEmpty() Method以一种只能通过查看代码无法预测的方式偏离此行为。 因此,我建议不要使用它,因为它会造成混乱,并且保存几个字符的好处很小。

一般来说,如果扩展方法在名称中有“null”或类似名称的话,我可以安全地调用null。 这样,我就明白了使用null调用它们可能是安全的。 此外,他们更好地在XML注释标题中记录了这一事实,因此当我将鼠标hover在调用上时,我会获得该信息。

使用“.IsNull()”将类实例与null进行比较时,甚至不比使用“== null”更短。 当没有约束的generics类型参数可以是值类型时,generics类中的事物会发生变化。 在这种情况下,与默认类型的比较是冗长的,我使用下面的扩展:

  public static bool IsDefault(this T x) { return EqualityComparer.Default.Equals(x, default(T)); }