扩展方法是一个非常有用的功能,您可以在任何类中添加许多您想要的功能。但我想知道是否有任何不利因素可能会给我带来麻烦。有什么意见或建议吗?
7 回答
- 扩展方法的导入方式(即一次整个命名空间)不是细化的。您无法从命名空间中导入一个扩展而不获取所有其他扩展。
- 从定义该方法的源代码中并不是很明显。这也是一个优势——这意味着您可以使您的代码看起来与该类型的其他方法一致,即使您出于某种原因不能将它放在同一个位置。换句话说,代码在高层次上更容易理解,但就正在执行的内容而言更复杂。我认为这对于一般的 LINQ 也是如此。
- 您只能拥有扩展方法,而不是属性、索引器、运算符、构造函数等。
- 如果您正在扩展 3rd 方类,并且在更高版本中他们引入了具有相同签名的新方法,您将不会轻易知道调用代码的含义已更改。如果新方法与您的扩展非常相似,但边界条件(或其他)略有不同,那么这可能会导致一些非常棘手的错误。不过相对不太可能发生。
几件事:
- 除非您在 VS.NET 中,否则扩展方法的来源并不总是很清楚
- 扩展方法无法通过反射或 C# 4.0 的动态查找来解决
扩展方法很有趣,但它们存在潜在的问题。例如,如果您编写了一个扩展方法,而另一个库创建了一个具有相同签名的扩展方法怎么办?您最终会在使用这两个名称空间时遇到困难。
此外,可以说它们不太容易被发现。我认为这取决于这一点。在某些情况下,您的代码应该封装在一个类中,而在其他情况下,可以将该功能添加为扩展方法。
我通常将扩展方法作为我自己的类或 BCL 类的包装器,并将它们放在不同的名称空间中。例如 Utils 和 Utils.Extensions。这样就不必使用扩展。
静态方法的空值检查是不同的。不一定更好或更坏 - 但不同,开发人员需要了解这一点。能够在空值上调用方法可能是出乎意料的,并且(有时)可能非常有用。
没有多态性(尽管支持重载)
如果源类型的两个扩展方法发生冲突(并且都不能称为“更好”),您可能会陷入歧义。然后编译器拒绝使用...这意味着在程序集 A 中添加扩展方法可能会破坏程序集 B 中不相关的代码。我已经看过几次了...
你不能在 C# 2.0 中使用,所以如果你正在为 C# 2.0 编写库,它没有帮助
它需要 [ExtensionAttribute] - 因此,如果您正在为 .NET 2.0 编写库,您会陷入困境:如果您声明自己的 [ExtensionAttribute],它可能会与 .NET 3.5 调用者发生冲突
不过不要误会我的意思——我是一个超级粉丝!
您可能会猜到我目前正在编写一个需要为 C# 2.0 和 .NET 2.0 调用者工作的库 - 并且(令人讨厌的)扩展方法将非常非常有用!
*=仅适用于编译器;已经编译的代码就可以了
就缺点而言,我认为它们有点像宏——你最终可能会得到更难维护的代码,因为其他人可能不熟悉你添加的扩展。
我很难想象在不熟悉现有代码的情况下添加一个方法。例如,您一直在使用第三方类来做某事,鉴于您不是原始开发人员并且您不知道事物在班级?做这样的事情几乎没有意义,就像在看不见的情况下开车毫无意义一样。在 LINQ 由 Microsoft 设计人员/代码实现的情况下,这很有意义,因为他们知道如何说序列的内部实现,现在他们希望通过添加 Count 方法来扩展其功能以计算所有序列中的元素。说了这么多,
扩展方法有两种不同的场景:
- 如果您想扩展来自外部程序集的任何类、接口结构,除了未标记为密封的类,您可以选择从这些类派生并使用其他方法扩展它们,您只有和我仅重复此选项可用。密封类和结构不能被继承,更不用说接口了。
然而,在您自己的程序集中,为特定的类或结构或接口提供扩展方法是一个非常主观的设计问题。如果您想对将使用您的程序集的人慷慨,您可以创建非常小的接口,理想情况下只有很少的属性,然后将一大堆扩展方法附加到它们。这是示例:
public interface IPointF{ float X { get; } float Y { get; } } public struct PointF: IPointF{ public PointF(float x, float y){ X=x;Y=y; } public float X { get; private set; } public float Y { get; private set; } } public static class PointHelper{ public static PointF ADD(this IPointF p, float val){ return new PointF(p.x+val, p.y+val); } }
因此,任何未来的用户都可以通过继承 IPoint 来节省大量的编码时间。