C# 扩展方法何时中断?

C# 扩展方法何时中断?,c#,.net,extension-methods,C#,.net,Extension Methods,我们目前正在讨论.NET中的扩展方法是否糟糕。或者在什么情况下扩展方法会引入难以发现的bug,或者以任何其他方式表现出意外行为 我们得出了: 为不受您控制的类型编写扩展方法(例如,使用GetTotalSize()扩展DirectoryInfo等)是不好的,因为API的所有者可能会引入一种隐藏扩展的方法,并且可能具有不同的边缘情况。例如,如果扩展方法由于隐藏而不再使用,那么在扩展方法中测试null将自动转换为NullReferenceException 问题: 除了“躲藏”之外,还有其他我们

我们目前正在讨论.NET中的扩展方法是否糟糕。或者在什么情况下扩展方法会引入难以发现的bug,或者以任何其他方式表现出意外行为

我们得出了:

  • 为不受您控制的类型编写扩展方法(例如,使用GetTotalSize()扩展DirectoryInfo等)是不好的,因为API的所有者可能会引入一种隐藏扩展的方法,并且可能具有不同的边缘情况。例如,如果扩展方法由于隐藏而不再使用,那么在扩展方法中测试null将自动转换为NullReferenceException
问题:

  • 除了“躲藏”之外,还有其他我们没有想到的危险情况吗
编辑:

另一种非常危险的情况。 假设您有一个扩展方法:

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;
        }
    }
}
你的考试会失败!它将编译时不会出现编译错误。它只会失败。甚至不必指定“using Example.ExtensionMethods.Conflict”。编译器将遍历名称空间名称,在Example.ExtensionMethods.Extension之前找到Example.ExtensionMethods.Conflict.ConflictExtension,并将使用该,而不会抱怨不明确的扩展方法。哦,真恐怖

有些奇怪之处:

  • 扩展方法可以在
    null
    实例上调用;这可能令人困惑(但有时很有用)
  • 如果他们有不同的意图,“隐藏”问题是个大问题
  • 同样,您可能会从两个不同的名称空间获得一个具有相同名称的不同扩展方法;如果您只有两个名称空间中的一个,这可能会导致不一致的行为(取决于哪个)
  • …但如果有人在代码使用的第二个命名空间中添加了类似的(相同的签名)扩展方法,它将在编译时中断(不明确)

edit)当然,还有“
Nullable
/
new()
”炸弹()…

What.Net调用扩展方法也是一种有限的形式(尝试忽略其中的php rant)


这将为您的讨论提供一些资料。

我们在项目中发现了一个缺陷:如果您编写了一个通用扩展方法,就不可能确保它适用于所有类型。我们有一个具有此签名的方法:

public static IEnumerable<T> Concat<T>(this T head, IEnumerable<T> tail)

由于
string.Concat
方法…

首先,我认为你的措辞有点误导。我想你说的是“类型”而不是“对象”


其次,扩展方法的最大优点是,您可以向不受控制的类型添加特性。如果您控制类型,为什么不直接修改类型而不是依赖扩展方法呢?

我不同意,扩展方法的全部要点是将您的成员添加到黑盒类中。与其他方法一样,也存在陷阱,您必须注意命名、实现和理解方法的优先顺序

我使用RubyonRails的时间几乎和使用C#的时间一样长。Ruby允许您执行类似于新扩展方法的操作。当然,如果有人将方法命名为相同的方法,可能会有潜在的问题,但是能够将方法添加到封闭类中的好处远远超过了潜在的好处(这可能是由于糟糕的设计或糟糕的规划造成的)。

您可以做一件事来确保扩展方法不会与其他方法冲突(扩展或其他)用于规则,例如。

我们团队的态度是,扩展方法非常有用,您无法实际禁止它们,但非常危险(主要是因为隐藏问题)因此,我们决定所有扩展方法名称都必须以
X
作为前缀(因此,我们有一系列
XInit…()
方法,以有用的方式初始化控件)。这样a)命名冲突的可能性降低了,并且b)程序员知道他使用的是扩展方法而不是类方法。

感谢object->type geek语法错误。好处是众所周知的。“我们在寻找危险。可以说,静态助手类在特定场景中同样有用,而且危险性要小得多。”Tobias最后编译的也是静态类。如果扩展方法和实例方法具有相同的名称,编译器必须做出决定。扩展方法有一个优雅之处。通常静态调用扩展方法会更好。在我相信它之前,我必须尝试一个新成员隐藏扩展方法的例子,而无需编译器的窥视!我认为D的“过载集”概念是正确的。
"foo".Concat(new [] { "tail" });