代码之家  ›  专栏  ›  技术社区  ›  Tobias Hertkorn

扩展方法何时中断?

  •  12
  • Tobias Hertkorn  · 技术社区  · 17 年前

    我们目前正在讨论.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));
            }
        }
    }
    

    请注意,使用它的名称空间更长。

    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,并将使用该名称空间 没有抱怨过模棱两可的扩展方法 . 哦,真恐怖!

    8 回复  |  直到 17 年前
        1
  •  9
  •   Community Mohan Dere    9 年前

    一些奇怪之处:

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

    编辑 )当然,还有一个 Nullable<T> new() see here )...

        2
  •  5
  •   Chuck Conway    17 年前

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

        3
  •  4
  •   Jon Skeet    17 年前

    MoreLINQ 项目:如果您编写一个泛型扩展方法,就不可能确保它可以处理所有类型。我们有一个具有此签名的方法:

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

    您不能将其用于:

    "foo".Concat(new [] { "tail" });
    

    因为 string.Concat 方法

        4
  •  2
  •   Matt Grande    17 年前

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

        5
  •  2
  •   Kit    15 年前

    要确保扩展方法不会与其他方法(扩展或其他)冲突,可以做的一件事是使用 FxCop 有这样的规则 Prevent Duplicate Extension Method Signatures .

        6
  •  1
  •   Brian Rasmussen    17 年前

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

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

        7
  •  1
  •   AAT    15 年前

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

        8
  •  1
  •   Cœur Gustavo Armenta    7 年前

    Net调用的扩展方法也是一种有限的形式 MonkeyPatching (尝试忽略其中的php语言)。