每个人都知道并喜欢String.IsNullOrEmpty(yourString)方法。
我想知道如果我们将String类扩展为具有这样的方法,是否会混淆开发人员或使代码更好:
yourString.IsNullOrEmpty();
亲:
缺点:
yourString
变量可以是null
,它看起来像
就像你在a上执行方法一样
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;
}
}
答案 0 :(得分:19)
如果我没有弄错的话,这里的每个答案都谴责扩展方法可以在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
是否为空的简写方法,并用非空字符串代替它。这样做可以显着减少代码,提高可读性和代码重用率。当然,你可以通过其他方式做到这一点,但这种方式不会比任何其他方式更令人困惑。例如:
var bar = foo.DoSomethingResultingInBar();
Console.Writeline(ToStringOr(bar, "[null]"));
当您遇到此代码时,您有什么不同于原始版本?您仍然需要检查代码,当bar
为空时,您仍然必须确定其行为。你仍然需要处理这种可能性。
扩展方法是否令人困惑?只有你不理解它们。而且,坦率地说,从代表到lambdas,语言的任何部分都可以这样说。
答案 1 :(得分:8)
我认为这个问题的根源是Jon Skeet在hates in his favorite language(C#)列表中提到的内容:C#不应该在整个命名空间中导入所有扩展方法自动。应该更明确地完成这个过程。
我个人对这个具体问题的看法是(因为我们无法对上述事实做任何事情)如果你愿意,可以使用扩展方法。我并不是说它不会令人困惑,但是这个关于扩展方法的事实(可以在null
引用上调用)是一个全局性的东西而且不仅仅影响String.IsNullOrEmpty
,所以C#devs应该熟悉它。
顺便说一句,幸运的是Visual Studio在IntelliSense工具提示中清楚地通过(extension)
识别扩展方法。
答案 2 :(得分:6)
我个人不喜欢这样做。现在扩展方法的最大问题是可发现性。你可以放弃了解特定类型中存在的所有方法,不可能查看方法调用并知道它是一种扩展方法。因此,我发现使用普通方法调用无法实现的扩展方法做任何事情都是有问题的。否则你最终会让开发人员感到困惑。
C ++中存在这个问题的必然结果。在几个C ++实现中,只要不触摸类型上的任何字段或虚方法,就可以在NULL指针上调用实例方法。我已经使用了几段有意执行此操作的代码,并在“this==NULL
”时为方法提供了不同的行为。与之合作非常令人发狂。
这并不是说我不喜欢扩展方法。恰恰相反,我喜欢它们并经常使用它们。但我认为写这些规则时应遵循两条重要规则。
编辑回应Mehrdad的评论
利用它的问题是我没有看到str.IsNullOrEmpty比String.IsNullOrEmpty(str)具有显着的功能优势。我看到的唯一优势是,一个人需要的打字少于另一个。关于扩展方法,通常也可以这样说。但在这种情况下,你还会改变人们对程序流程的看法。
如果更短的打字是人们真正想要的,那么IsNullOrEmpty(str)是不是更好的选择?这既是明确的,也是最短的。 True C#今天不支持这样的功能。但想象一下,如果在C#中,我可以说
using SomeNamespace.SomeStaticClass;
这样做的结果是SomeStaticClass上的所有方法现在都在全局命名空间中并且可用于绑定。这似乎是人们想要的,但他们将它附加到我不是很喜欢的扩展方法。
答案 3 :(得分:5)
我非常喜欢这种方法,因为该方法清楚地表明它正在检查对象是否为空。 ThrowIfNull,IsNull,IsNullOrEmpty等。它非常易读。
答案 4 :(得分:4)
就个人而言,我不会创建一个扩展来执行框架中已存在的扩展,除非它是可用性的重大改进。在这种情况下,我不认为是这种情况。此外,如果我要创建一个扩展,我会以减少混淆的方式命名,而不是增加它。我再次认为这个案例没有通过测试。
说了这么多,我确实有一个字符串扩展来测试,不仅是字符串是null还是空,还要测试它是否只包含空格。我称之为IsNothing
。你可以找到它here。
答案 5 :(得分:1)
它看起来不像是在调用null变量上的方法。你/是/调用null变量的方法,虽然是通过静态扩展方法实现的。我不知道扩展方法(我已经很了解)支持它。这甚至可以让你做一些疯狂的事情:
public static int PowerLength(this string obj)
{
return obj == null ? 0 : obj.Length;
}
从我现在所处的位置开始,我会在认为有害下的
答案 6 :(得分:1)
是的,它会混淆。
我认为每个知道IsNullOrEmpty()
的人都认为使用它是很自然的。因此,建议的扩展方法不会增加可读性。
也许对于.NET新手来说,这种扩展方法可能更容易处理,但是 danger 可能会导致她/他不了解扩展方法的所有方面(就像你一样)需要导入命名空间,并且可以在null上调用它。她/他可能想知道为什么这个扩展方法在另一个项目中不存在。无论如何:即使有人不熟悉.NET,IsNullOrEmpty()
方法的语法可能会很快变得自然。此外,扩展方法的好处不会超过它引起的混乱。
编辑:尝试改写我想说的内容。
答案 7 :(得分:1)
让我们来看看pro:s和con:s ......
- 更具可读性。
醇>
是的,稍微一点,但是看起来你在一些不必是实例的东西上调用实例方法这一事实超过了可读性的提高。实际上它更容易阅读,但它更难理解,因此提高可读性实际上只是一种幻觉。
- 少打字。
醇>
是的,但这确实不是一个有力的论据。如果打字是编程的主要部分,那么你就不会做一些具有足够远程挑战性的东西,以便你作为开发人员发展。
- 可能令人困惑,因为yourString变量可以为null,看起来像 你正在对null变量执行方法。
醇>
是的,(如上所述)。
答案 8 :(得分:0)
我认为使用“IsNull”类型调用扩展任何对象有点令人困惑,如果可能应该避免使用。
有争议的是,IsEmptyString方法可能对String类型有用,并且因为你通常将它与null的测试结合起来,IsNullOrEmpty可能是有用的,但我也避免这样做,因为字符串类型已经有一个静态方法来执行此操作,我不确定你是在节省多少打字(最多5个字符)。
答案 9 :(得分:0)
对null为null的变量调用方法通常会导致NullReferenceException。 IsNullOrEmpty()
- 方法以一种只能通过查看代码无法预测的方式偏离此行为。因此,我建议不要使用它,因为它会造成混乱,保存几个字符的好处很小。
答案 10 :(得分:0)
一般情况下,如果扩展方法的名称中包含“null”或类似名称,则可以安全地调用null。这样,我就明白了用null调用它们是安全的。此外,他们更好地在XML注释标题中记录了这一事实,因此当我将鼠标悬停在通话上时,我会得到该信息。
答案 11 :(得分:0)
使用“.IsNull()”将类实例与null进行比较时,甚至不比使用“== null”更短。 当没有约束的泛型类型参数可以是值类型时,通用类中的事情会发生变化。 在这种情况下,与默认类型的比较是冗长的,我使用下面的扩展名:
public static bool IsDefault<T>(this T x)
{
return EqualityComparer<T>.Default.Equals(x, default(T));
}