我们目前正在讨论.NET中的扩展方法是否错误。或者在什么情况下,扩展方法可能会导致难以发现的错误或以任何其他方式出现意外行为。
我们想出了:
问题:
编辑:
另一个非常危险的情况。 假设您有一个扩展方法:
namespace Example.ExtensionMethods
{
public static class Extension
{
public static int Conflict(this TestMe obj)
{
return -1;
}
}
}
并使用它:
namespace Example.ExtensionMethods.Conflict.Test
{
[TestFixture]
public class ConflictExtensionTest
{
[Test]
public void ConflictTest()
{
TestMe me = new TestMe();
int result = me.Conflict();
Assert.That(result, Is.EqualTo(-1));
}
}
}
请注意,您使用它的命名空间更长。
现在你引用一个dll:
namespace Example.ExtensionMethods.Conflict
{
public static class ConflictExtension
{
public static int Conflict(this TestMe obj)
{
return 1;
}
}
}
你的测试将失败!它将编译而没有编译器错误。它会完全失败。没有你甚至不必指定“使用Example.ExtensionMethods.Conflict”。编译器将遍历命名空间名称并在Example.ExtensionMethods.Extension之前找到Example.ExtensionMethods.Conflict.ConflictExtension,并将使用而不会抱怨模糊扩展方法。哦,恐怖!
答案 0 :(得分:9)
一些好奇心:
null
个实例上调用扩展方法;这可能令人困惑(但有时很有用)(编辑)当然,还有“Nullable<T>
/ new()
”炸弹(see here)......
答案 1 :(得分:5)
我不同意,扩展方法的重点是将您的成员添加到黑盒子类中。像其他一切都有陷阱,你必须注意命名,实现和理解方法的啄食顺序。
答案 2 :(得分:4)
我们刚刚在MoreLINQ项目中发现了一个破坏:如果你编写一个通用的扩展方法,就不可能确保它适用于所有类型。我们有一个带有此签名的方法:
public static IEnumerable<T> Concat<T>(this T head, IEnumerable<T> tail)
您不能将其用于:
"foo".Concat(new [] { "tail" });
因为string.Concat
方法......
答案 3 :(得分:2)
我使用Ruby on Rails几乎和我使用C#一样长。 Ruby允许您执行类似于新扩展方法的操作。当然,如果有人将方法命名为相同的话,那么潜在的问题,但是能够将方法添加到封闭类中的优势远远超过潜在的不利因素(这可能是由于糟糕的设计或糟糕的计划造成的)。 p>
答案 4 :(得分:2)
要确保扩展方法与其他方法(扩展名或其他方法)不冲突,您可以做的一件事是将FxCop与Prevent Duplicate Extension Method Signatures等规则一起使用。
答案 5 :(得分:1)
什么.Net调用扩展方法也是一种限制形式MonkeyPatching(试着忽略那里的php咆哮)。
这应该会为你的讨论提供一些材料。
答案 6 :(得分:1)
首先,我认为你的措辞有点误导。我认为你说的是“类型”,而不是“对象”。
其次,扩展方法的一大优势是您可以添加要控制的类型的功能。如果您控制类型,为什么不修改类型而不是依赖于扩展方法?
答案 7 :(得分:1)
我们在团队中采取的态度是,扩展方法非常有用,你无法实际禁止它们,但是如此危险(主要是因为隐藏问题)你必须要谨慎一点。因此,我们决定所有扩展方法名称必须以X
为前缀(因此我们有一堆XInit...()
方法以有用的方式初始化控件,例如)。那样的方式a)减少了命名冲突的可能性,并且b)程序员知道他正在使用扩展方法而不是类方法。