添加成员如何降低 类 的向后兼容性?

How does adding members reduce backwards compatibility of classes?

我目前正在学习 C# 扩展方法。我在几个地方读到,向 classes 添加成员会降低使用这些 classes 的代码的向后兼容性。

我在这里读到这个: https://blogs.msdn.microsoft.com/vbteam/2007/03/10/extension-methods-best-practices-extension-methods-part-6/

还有 Troelson 的 Pro C# 书的第 418 页。

恐怕这对我来说没有意义。当然,在添加额外成员之前使用这些 classes 实例的任何代码(不使用扩展方法,只需将它们添加到 class),仍然能够调用所有旧的方法、属性、字段和构造函数与以前一样,因为它们没有改变。即使新成员可以改变对象的状态,它们也永远不会在旧代码中被调用,因此代码是向后兼容的。

我在这里没有看到什么?

这是添加新方法实际上可能会破坏客户端代码的一种可能方式...

void Main()
{
    var oldFoo = new OldFoo();
    var oldResult = oldFoo.Calculate(2, 2); // 4
    var newFoo = new NewFoo();
    var newResult = newFoo.Calculate(2, 2); // 0
}

public class OldFoo
{
    public int Calculate(params int[] values)
    {
        return values.Sum();
    }
}

public class NewFoo
{
    public int Calculate(params int[] values)
    {
        return values.Sum();
    }

    public int Calculate(int value1, int value2)
    {
        return value1 - value2;
    }
}

还有另一种方法,专门处理扩展方法...

最初,客户端定义了一个扩展方法,使 Foo 能够 Combine:

void Main()
{
    var foo = new Foo();
    var result = foo.Combine(2, 2); // "22"
}

public static class Extensions // added by client
{
    public static string Combine(this Foo foo, params int[] values)
    {
        return string.Join(string.Empty, values.Select(x => x.ToString()));
    }
}

public class Foo { }

后来,Foo 的开发者在 class 中添加了一个新的 Combine 方法:

void Main()
{
    var foo = new Foo();
    var result = foo.Combine(2, 2); // 4
}

public static class Extensions
{
    public static string Combine(this Foo foo, params int[] values)
    {
        return string.Join(string.Empty, values.Select(x => x.ToString()));
    }
}

public class Foo
{
    public int Combine(params int[] values)
    {
        return values.Sum();
    }
}

请注意,扩展方法被新的 Combine 实例方法有效地阻止或隐藏

现实世界的类比可能会有所帮助。把它想象成机器。想象一下,有人设计了一种发动机,人们开始将其用作某些设备(比如收割机)的基础。如果发动机设计师认为燃油滤清器会有帮助并将其添加进去,它可能会破坏收割机设计,因为该设计可能已经在 space 现在被新燃油滤清器占用的空间中放置了一些东西。

添加新成员(燃油泵)降低了向后兼容性。收割机设计基于历史上落后的版本。

还有一个更基于编程的示例:应用程序设计者为其应用程序创建一个以特定方式运行的解析器。发布后,发现解析器在一些不常见的情况下没有正确实现规范。发布了一个正确实现规范的新版本,但添加了一个标志以提供以前的行为。

重点是扩展方法可以与成员方法共享同一个命名空间,如果它们共享,则成员方法完全按名称优先。这意味着您作为库开发人员可能会破坏在他自己的应用程序中向您的 class 引入扩展方法的客户的代码。在你不知道你在做的情况下。

如果您使用新的成员方法更新您的库 class 并且您的客户安装更新,他可能会发现您的新方法与他之前添加的扩展方法同名。或者,如果参数列表兼容,他可能找不到它。他的扩展方法现在将被您的新成员方法隐藏。他的代码现在将无法编译(不兼容的参数列表),或者更糟的是,行为会有所不同。