【问题标题】:Why does the ordering of the unsealed class`s unsealed virtual methods calls matter?为什么未密封类的未密封虚拟方法调用的顺序很重要?
【发布时间】:2019-12-02 23:19:39
【问题描述】:

为什么未密封类的未密封虚拟方法调用的顺序很重要?

我正在探索 CLR via C# 一书,我遇到了以下摘录:

当一个类原本是密封的,以后可以改为非密封的 破坏兼容性。但是,一旦一个类被解封,就永远不能将其更改为sealed in 未来,因为这会破坏所有派生类。 另外,如果未密封的类定义了任何 未密封的虚拟方法,虚拟方法调用的顺序必须使用 new 维护 版本或将来有可能破坏派生类型。

有人可以以万无一失的方式解释以粗体突出显示的部分并(也许)提供一些示例吗?

我了解什么是密封/非密封类/方法,我了解什么是虚拟方法。但我不明白的是订购。摘录中提到的顺序是什么?

【问题讨论】:

  • 我已经使用该框架超过 15 年(其中近十二个在微软的开发人员支持组织中 - 那时的方式)并且从未考虑过这一点;这几乎是一个边缘案例。我猜是方法调用是通过表调用来完成的。如果您在一个类型上定义了三个方法(例如 M1、M2 和 M3),那么方法表中有三个插槽,第一个包含 M1 等。如果您在基类中重新排序方法(并且不要' t 重新编译派生类),那么 slot1 最终可能会引用 M3,而对 M1 的派生类调用可能会调用 M3
  • 这就是我的意思:假设您有两个虚拟方法VM1()VM2()。您的未密封类还有一个方法M1(),它调用这两个虚拟方法。如果M1()VM1() 后跟VM2() 的顺序调用这两个虚方法,则您不能突然将M1() 切换到现在在VM1() 之前调用VM2() 而不会破坏派生类。
  • 虚拟方法的调用甚至不需要被限制在单个方法中。考虑这样一种情况:被覆盖的VM1() 做了一些被覆盖的VM2() 依赖的设置。
  • 如有帮助,本书可在线免费获取books.google.de/…
  • 我认为@Flydog57 是正确的,到目前为止的答案并没有解决这个问题......我不认为里希特正在访问 SO,这里唯一真正的选择是如果 Eric Lippert 看到这个问题和答案...

标签: c# clr virtual sealed sealed-class


【解决方案1】:

这是一个维护虚方法调用顺序很重要的场景:

class BaseClass
{
    public int Answer { get; protected set; }

    protected virtual void VM1() { Answer += 20; }
    protected virtual void VM2() { Answer += 10; }

    public void Init() { VM1(); VM2(); }
}

class DerivedClass : BaseClass
{
    private int _dividend;

    protected override void VM1() { Answer = _dividend = 20; }
    protected override void VM2() { Answer /= 10 }
}

现在假设你在某个地方有这个:

var baseObj = new BaseClass();
baseObj.Init();
int baseAnswer = baseObj.Answer;

var derivedObj = new DerivedClass();
derivedObj.Init();
int derivedAnswer = derivedObj.Answer;

baseAnswer 将包含30derivedAnswer 将包含2

现在,假设 Init() 已更改,因此在 VM1() 之前调用了 VM2()baseAnswer 仍然包含 30 所以一切看起来都还不错。但是,derivedAnswer 将包含20(它是2)!这就是我相信这本书警告你的那种情况。

【讨论】:

  • 没有添加/删除密封属性的关系。 “需要维护”是指派生类需要维护/更改/更新。如果 Base 类是密封的,则从 Init 到 VM1 的调用不再是虚拟的。肯定的。 “破坏类型”并不意味着计算不同的数字,它意味着由于版本冲突而无法加载应用程序,或者在运行时引发奇怪的 TypeLoadExceptions。
【解决方案2】:

这是关于更改源代码 - 而不是构建类层次结构。

C# 中没有“未密封”关键字,您不能从密封类派生类并以任何方式声明使密封“撤消”。

您可以通过更改源代码来删除关键字“sealed” - 这是“unsealing”并且他们强调,这是一个非破坏性更改。所有引用您的代码的库都可以使用。

这本书必须是关于代码审查或软件维护,或库的演变而不是编程。

【讨论】:

  • 能否请您在回答中提供更多详细信息? unsealed 关键字的缺失与排序有何关系?而且我仍然不明白(从您的回答中)摘录中提到的顺序。
  • 阅读其他cmets关于您的问题。我不确定你是否理解这个场景。没有未密封的关键字,因此不能缺席。我不确定你理解他们所说的开封是什么意思。这是关于编写一个库,将其分发给不同的用户,5 年后你修改了库,你进行了更改。现在他们讨论这些库的用户在什么情况下只需要交换 DLL,或者他们必须重新编译自己的代码。重新排序虚拟方法可能是一个重大变化。我不这么认为,有时书是错的,但你永远不会遇到这种情况。
  • @Holger 我在评论中提出的场景肯定会遇到。
  • 对方法的所有调用都以字符串表示形式保存在文件中。顺序应该不重要。
  • @Holger 您在警告中缺少“呼叫”一词。调用虚方法的顺序绝对重要。没错,你定义虚方法的顺序不是。
猜你喜欢
  • 1970-01-01
  • 2022-09-25
  • 2012-05-19
  • 1970-01-01
  • 1970-01-01
  • 2016-02-23
  • 1970-01-01
  • 2021-12-06
  • 2019-09-24
相关资源
最近更新 更多