1

Why does the ordering of the unsealed class`s unsealed virtual methods calls matter?

I am exploring the CLR via C# book and I come across the following excerpt:

When a class is originally sealed, it can change to unsealed in the future without breaking compatibility. However, once a class is unsealed, you can never change it to sealed in the future as this would break all derived classes. In addition, if the unsealed class defines any unsealed virtual methods, ordering of the virtual method calls must be maintained with new versions or there is the potential of breaking derived types in the future.

Could someone explain the highlighted in bold part in a foolproof manner and (maybe) provide a few examples?

I understand what is the sealed/unsealed class/method and I understand what is a virtual method. But the thing which I do not understand is the ordering. What ordering is referred to in the excerpt?

qqqqqqq
  • 1,831
  • 1
  • 18
  • 48
  • 4
    I've been working with the Framework for >15 years (nearly a dozen of them in Microsoft's developer support organization - way back then) and never thought about this; it's pretty much an edge case. What I'm guessing is that method calls are done by calling through a table. If you have three methods defined on a type (say M1, M2 and M3), then there are three slots in the method table, the first containing M1, etc. If you re-order the methods in a base class (and don't recompile the derived classes), then slot1 may end up referring to M3, and derived class calls to M1 may call M3 – Flydog57 Dec 02 '19 at 23:27
  • 1
    This is what I take it to mean: Let's say you have two virtual methods `VM1()` and `VM2()`. Your unsealed class also has a method `M1()` that calls those 2 virtual methods. If `M1()` calls both virtual methods in the order `VM1()` followed by `VM2()`, you can't suddenly switch `M1()` to now call `VM2()` before `VM1()` without possibly breaking derived classes. – itsme86 Dec 02 '19 at 23:31
  • The calling of the virtual methods doesn't even need to be constrained to a single method. Consider a situation where overridden `VM1()` does some setup that overridden `VM2()` relies on. – itsme86 Dec 02 '19 at 23:48
  • For case it helps, the book is available online for free https://books.google.de/books?id=36tCAwAAQBAJ&pg=PT276&lpg=PT276&f=false#v=onepage&q&f=false – Holger Dec 03 '19 at 00:03
  • I think @Flydog57 is right and answers so far do not address the question... I don't think Richter is visiting SO, the only real option here is if Eric Lippert sees this question and answers... – Alexei Levenkov Dec 03 '19 at 01:20
  • @Flydog57, the order in which you define members is significant is when defining a managed interface that maps to a com interface, but as far as I am aware, in all other cases the JIT manages the vtable slot positions and matches the methods based on things other than the order of definition (uses method name and a signature blob). – Brian Reichle Dec 07 '19 at 00:58

2 Answers2

1

This is about changing your source code - not about building a class hierarchy.

There is no "unsealed" keyword in C#, you cannot derive a class from a sealed class and declare make the sealed "undone" in any way.

You can remove the keyword "sealed" by changing your source code - this is "unsealing" And they underline, this is a non-breaking-change. All libraries referring to your Code will work on.

This book must be about Code-Review or Software-Maintenance, or Evolution of libraries not about programming.

Holger
  • 2,446
  • 1
  • 14
  • 13
  • Could you, please, provide more details in your answer? How the absence of the **unsealed** keyword is related to the ordering? And I still do not understand (from your answer) what ordering is referred to in the excerpt. – qqqqqqq Dec 02 '19 at 23:32
  • Read the other comments on your questions. I'm not sure you understand the scenario. There is no unsealed keyword, so it cannot be absent. I'm not sure you understand what they mean by unsealing. It's about programming a library , distributing it to different user, and 5 years later you modify the library, you make changes. Now they discuss in what scenarios the users of those libraries, have just to exchange the DLL, or they have to recompile their own Code. Re-Ordering Virtual Methods might be a breaking change. I don't think so, sometimes books are wrong, but you will never encounter this. – Holger Dec 02 '19 at 23:41
  • @Holger The scenario I put forth in my comment could definitely be encountered. – itsme86 Dec 02 '19 at 23:44
  • All calls to Methods are saved in string-representation in the file. The order should not be important. – Holger Dec 02 '19 at 23:44
  • @Holger You're missing the word "calls" in the warning. The order in which you call virtual methods is definitely important. The order in which you define virtual methods, you're right, is not. – itsme86 Dec 02 '19 at 23:45
  • @itsme86. You are right, change "never" to "very unlikely". Maybe the compiler is doing some optimization for virtual methods of a sealed class, cause he knows for sure, what to call. Maybe he is calling it like a simple method, and this call would change on the remove of the sealed attribute. – Holger Dec 02 '19 at 23:48
  • @Holger I think you're still not understanding the scenario I'm setting. I'm talking about the base class calling the virtual methods and maintaining the order the base class calls them in. – itsme86 Dec 02 '19 at 23:53
  • @itsme86. The order of calls is always important. Math.Sqrt(Math.Round(3.3)) is different from Math.Round(Math.Sqrt(3.3)). Even on static methods. Maybe you define some sample code in your own answer, to be more elaborate on your scenario. I often check the IL-Level and there every call is just string-based. – Holger Dec 02 '19 at 23:53
  • @Holger Hopefully my answer better illustrates what I was trying to say. – itsme86 Dec 03 '19 at 00:07
  • Yes, I consider it off topic, but we are just guessing around. One should go and ask Jeffrey Richter. Stackoverflow would rate the "advantages of sealed classes" as opinion-based. – Holger Dec 03 '19 at 00:20
1

Here's a scenario where maintaining the order of virtual method calls is important:

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 }
}

Now let's say you have this somewhere:

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

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

baseAnswer will contain 30 and derivedAnswer will contain 2.

Now, let's say Init() was changed so VM2() was called before VM1(). baseAnswer still contains 30 so everything looks okay. However, derivedAnswer will contain 20 (it was 2)! This is the kind of situation that I believe the book is warning you about.

itsme86
  • 19,266
  • 4
  • 41
  • 57
  • There is no relation of adding/removing a sealed attribute. And "Maintenance necessary" means, the derived class needs to be maintained/changed/updated. If Base class is sealed, the call from Init to VM1 is not virtual any more. Definitly. "breaking types" does not mean to compute different numbers, it means unability to load the application due to version conflicts, or raising weird TypeLoadExceptions at runtime. – Holger Dec 03 '19 at 00:13