【问题标题】:How does adding members reduce backwards compatibility of classes?添加成员如何降低类的向后兼容性?
【发布时间】:2016-04-08 17:13:18
【问题描述】:

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

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

以及 Troelson 的 Pro C# 书籍的第 418 页。

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

我在这里没有看到什么?

【问题讨论】:

  • 您看不到将类型的实例调用给定方法时运行的代码从一种实现更改为另一种实现可能会以某种方式影响该代码吗?
  • 我不认为它改变了代码,而是添加到类中。显然,我可以看到摆弄其他类所依赖的代码是危险的。但是,添加一个方法重载将优先于某些参数的原始方法(可以在旧代码方法调用中使用),就像下面接受的答案一样,这是一个明确的例子,说明我没有可能出错想到了。
  • @GeorgeWooding,在我的回答中添加了第二个示例。
  • @GeorgeWooding 您不认为将您为程序编写的实现替换为由其他人编写的完全不同的实现,可能是出于完全不同的目的,这并没有改变程序?并且该答案中的示例与您链接的文章中描述的完全无关,该文章描述了它的行为。
  • @Servy,我相信第二个例子更相关,但如果我错了,请纠正我。

标签: c# oop extension-methods backwards-compatibility


【解决方案1】:

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

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的开发者在类中增加了一个新的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 实例方法有效地阻止或遮蔽

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-14
        相关资源
        最近更新 更多