使用扩展:权衡利弊与利弊

时间:2009-02-15 21:06:18

标签: c# extension-methods

最近我asked a question关于如何清理我认为丑陋的代码。一个建议是创建一个扩展方法,它将执行所需的功能并返回我想要的功能。我的第一个想法是'太棒了! Extensions有多酷......“但经过一番思考后,我开始重新考虑使用Extensions ......

我主要担心的是,Extensions似乎是一种自定义的“快捷方式”,可以让其他开发人员难以遵循。我理解使用扩展可以帮助使代码语法更容易阅读,但是如何跟随幕后的声音呢?

以我之前的问题代码片段为例:

if (entry.Properties["something"].Value != null)
  attribs.something = entry.Properties["something"].Value.ToString();

现在用扩展名替换它:

public static class ObjectExtensions
{
    public static string NullSafeToString(this object obj)
    {
        return obj != null ? obj.ToString() : String.Empty;
    }
}

并使用语法调用:

attribs.something = entry.Properties["something"].Value.NullSafeToString();

定义一个方便的方法,但它真的值得另一个类对象的开销吗?如果有人想重用我的代码片段而不理解扩展,会发生什么?我可以像使用相同的结果一样轻松地使用语法:

attribs.something = (entry.Properties["something"].Value ?? string.Empty).ToString()

所以我做了一些挖掘,发现了几篇关于使用Extensions的优点/缺点的文章。对于那些倾向于看看以下链接:

MSDN: Extension Methods

Extension Methods Best Practice

Extension Methods

我无法确定哪种方式更好。自定义扩展执行我想要他们做的事情或更多显示的代码来完成相同的任务?我真的很想知道开发人员对这个主题的看法......

5 个答案:

答案 0 :(得分:17)

就我个人而言,我认为扩展方法可读性的“问题”被夸大了。如果您专注于使代码易于阅读它正在做什么,那么大多数情况下,这比 它正在做什么更重要。如果开发人员想要追踪并了解幕后实际发生的事情,他们总是可以点击进行实施。

扩展方法的主要问题是它们的发现方法 - 即通过指定的命名空间而不是指定的类。但这是另一回事:)

我并不是建议你随意添加扩展方法,但我会认真考虑你需要知道方法中每个表达式的工作方式,而不是通过它来查看它在广义上的作用。

编辑:您对术语的使用可能会误导您。没有“扩展对象”这样的东西 - 只有“扩展方法”,它们必须存在于静态类型中。因此,您可能需要引入新的类型,但您不再创建对象

答案 1 :(得分:6)

[OP]定义一个方便的方法,但它真的值得另一个类对象的开销吗?

在此方案中不会创建额外的类对象。在引擎盖下,扩展方法与静态方法没有区别。扩展方法容器有一个额外的元数据条目,但这是非常小的。

[OP]如果有人想重用我的代码片段而不理解扩展对象,会发生什么?

那么现在是教育他们的好时机:)。是的,新开发人员可能不习惯使用扩展方法启动。但这不是一个孤立的特征。它在我在内部和网络上看到的所有代码示例中的使用越来越多。对于开发人员来说,这是绝对值得学习的东西。我不认为它符合“让人们知道的深奥”的类别

答案 2 :(得分:4)

扩展方法中唯一需要处理的非常奇怪的事情是:

  1. 如果左侧(您正在调用方法的对象)为null,则它们不必导致空引用异常。
    • 有时可能有用,但与预期相反,因此应谨慎使用。
  2. 通过对它们适用的类/接口的反射无法访问它们。
    • 一般不是问题,但值得记住。
  3. 与其他扩展方法的名称冲突涉及冗长的解决方案规则序列
    • 如果你关心顺序是喜欢:
      1. 当前模块中定义的扩展方法。
      2. 在当前命名空间或其任何一个父节点中的数据类型内定义的扩展方法,子命名空间的优先级高于父命名空间。
      3. 在当前文件中的任何类型导入中定义的扩展方法。
      4. 当前文件中任何名称空间导入内定义的扩展方法。
      5. 在任何项目级类型导入中定义的扩展方法。
      6. 在任何项目级命名空间导入中定义的扩展方法。

答案 3 :(得分:2)

[OP]如果有人想重用我的代码片段而不理解扩展对象,会发生什么?

如果实现它们的程序集不是项目中的引用,则扩展方法不会在对象的intellisense中显示。您的代码段也无法编译。这可能会给其他开发者带来一些混乱。

如果引用了扩展方法程序集,它将在intellisense中显示,但在对象的文档中不会提及。这可能会引起一些混乱。

然而,正如@JaredPar所提到的,作为一种技术的扩展方法被越来越多地使用,我希望大多数C#程序员能够了解它们。因此,我不会太担心任何可能的混淆。

答案 4 :(得分:0)

C#Extensions是.Net提供的一个额外的“工具”,以帮助您更好地编写代码。它们的另一个优点是,它们处理null。虽然它们似乎非常实用,但我尝试仅在某些情况下使用它们才能真正整理我的代码,因为它们不是标准的编码方法,它们与其他类有点分离,因为它们必须在静态类中并且是静态自己。

让我们说它们的实现有点凌乱,但它们的使用更加整洁

同样重要的是要提到它们只存在于C#和VB.Net中(Java没有扩展)。另一个重要的事实是Extensions没有优先于标准方法,这意味着如果一个方法是在一个与同一个类上的扩展方法同名的类中实现的,那么第一个方法是将被调用的方法,而不是扩展方法。

下面有三种情况我经常使用它们,为什么我使用它们以及可以解决同样问题的替代解决方案:

<强> 1。实现泛型类的特定方法: 我有一个泛型类型,比如说集合List<T>。我想做一个仅适用于特定类型列表的方法。假设使用分隔符从字符串列表创建联合的方法 ("A", "B", "C", " sep " --> "A sep B sep C"):

public static string union(this List<string> stringList, String seperator)
{
   String unionString = "";
   foreach (string stringItem in stringList) {
      unionString += seperator + stringItem; }
   if (unionString != "") { 
      unionString = unionString.Substring(seperator.Length); }
   return unionString;
}

如果我不想使用扩展程序,我必须创建一个新类“StringCollection : List<string>”并在那里实现我的方法。这主要不是问题,在大多数情况下实际上更好,但并非在所有情况下都是如此。例如,如果您在很多情况下接收字符串列表中的所有数据,则每次要使用union时都不必在StringCollections中转换这些列表,而是使用扩展名。

<强> 2。要实现需要处理null的方法: 我需要一个方法将对象转换为字符串而不抛出异常,以防对象为空

public static String toStringNullAllowed(this Object inputObject)
{
   if (inputObject == null) { return null; }
   return inputObject.ToString();
}

如果我不想使用扩展,我将不得不创建一个类(可能是静态的),例如StringConverter,它将执行相同的工作,使用更多的单词而不是简单的{{ 1}}

第3。扩展值类型或密封类: 诸如int,float,string等的值类型以及密封类(无法继承的类)不能通过继承来扩展。下面你可以看到一个扩展整数的例子,可以转换为x位数字(例如myObject.toStringNullAllowed();):

integer 34, digits 5 --> "00034"

另一个替代解决方案是静态类(如工具箱),让我们说“数学”。

  • 在这种情况下,你会写:public static String toXDigit(this int inputInteger, int x) { String xDigitNumber = inputInteger.ToString(); while (xDigitNumber.Length < x) { xDigitNumber = "0" + xDigitNumber; } return xDigitNumber; }
  • 使用扩展方法:Math.toXDigit(a, x);

扩展方法看起来更好,更容易理解, 喜欢说英语

总而言之,我认为扩展的缺点是它们的实现与标准类分离,对于不习惯它们的程序员看起来有点奇怪或困难,而它们的优点是它们提供了更多可理解的,更整洁和封装的语言使用。