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

C# 为什么未密封类的未密封虚拟方法调用的顺序很重要?,c#,clr,virtual,sealed,sealed-class,C#,Clr,Virtual,Sealed,Sealed Class,为什么未密封类的未密封虚拟方法调用的顺序很重要 我正在通过C#书探索CLR,我发现了以下摘录: 当一个类最初被密封时,它可以在将来更改为未密封,而无需修改 破坏兼容性。然而,一旦一个类被解封,您就永远不能将其更改为密封的 未来,因为这将破坏所有派生类此外,如果未密封类定义了 未密封的虚拟方法,必须使用新的 版本或将来可能会中断派生类型。 有人能用简单的方式解释突出显示的粗体部分,并(也许)提供几个例子吗 我了解什么是密封的/未密封的类/方法,我了解什么是虚拟方法。但我不明白的是顺序。摘录中提到了

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

我正在通过C#书探索CLR,我发现了以下摘录:

当一个类最初被密封时,它可以在将来更改为未密封,而无需修改 破坏兼容性。然而,一旦一个类被解封,您就永远不能将其更改为密封的 未来,因为这将破坏所有派生类此外,如果未密封类定义了 未密封的虚拟方法,必须使用新的 版本或将来可能会中断派生类型。

有人能用简单的方式解释突出显示的粗体部分,并(也许)提供几个例子吗


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

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

在C#中没有“unchaled”关键字,您不能从密封的类派生类并以任何方式声明使密封的“undone”

您可以通过更改源代码来删除关键字“sealed”-这是“unchaling”,它们加下划线,这是一个非破坏性的更改。所有引用您的代码的库都可以使用


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

下面是一个场景,其中维护虚拟方法调用的顺序非常重要:

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
将包含
30
derivedswer
将包含
2


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

我已经在该框架上工作了超过15年(当时在微软的开发人员支持组织中有近12年),从未想过这一点;这几乎是一个边缘案件。我猜方法调用是通过表调用完成的。如果在一个类型上定义了三个方法(比如M1、M2和M3),那么方法表中有三个插槽,第一个插槽包含M1等。如果在基类中对方法重新排序(并且不重新编译派生类),那么slot1可能最终引用M3,对M1的派生类调用可能会调用m3这就是我的意思:假设您有两个虚拟方法
VM1()
VM2()
。未密封的类还有一个方法
M1()
,该方法调用这两个虚拟方法。如果
M1()
VM1()
后跟
VM2()
的顺序调用这两个虚拟方法,那么您不能突然将
M1()
切换到现在调用
VM2()
之前的
VM1()
,而不可能中断派生类。虚拟方法的调用甚至不需要约束到单个方法。考虑一个重写<代码> VM1()>代码>的情况:一些设置被重写<代码> vM2-()/<代码>。如果有帮助的话,这本书是免费在线的,我想“FielDo57是对的,到目前为止还没有回答这个问题……”我不认为里克特是来访问的,所以,这里唯一真正的选择是如果埃里克·利珀特看到了这个问题和答案……请你在回答中提供更多细节好吗?未密封关键字的缺失与排序有何关系?我仍然不明白(从你的回答)摘录中提到了什么顺序。请阅读关于你问题的其他评论。我不确定你是否理解这种情况。没有未密封的关键字,因此它不能不存在。我不确定你是否明白他们说的开封是什么意思。它是关于编写一个库,将它分发给不同的用户,5年后你修改库,你进行更改。现在,他们讨论了在什么情况下,这些库的用户必须交换DLL,或者必须重新编译自己的代码。对虚拟方法重新排序可能是一个突破性的变化。我不这么认为,有时候书是错的,但你永远不会遇到这种情况。@Holger我在评论中提出的场景肯定会遇到。对方法的所有调用都以字符串表示形式保存在文件中。顺序不应该很重要。@Holger您在警告中漏掉了“calls”一词。调用虚拟方法的顺序绝对重要。您定义虚拟方法的顺序是不正确的。添加/删除密封属性没有关系。“必要维护”是指需要维护/更改/更新派生类。如果基类是密封的,则从Init到VM1的调用不再是虚拟的。当然。“中断类型”并不意味着计算不同的数字,它意味着由于版本冲突而无法加载应用程序,或者在运行时引发奇怪的TypeLoadException。