As far as I know making class sealed
gets rid of look up in VTable or am I wrong? If I make a class sealed
does this mean that all virtual methods in class hierarchy are also marked sealed
?
For example:
public class A {
protected virtual void M() { ........ }
protected virtual void O() { ........ }
}
public sealed class B : A {
// I guess I can make this private for sealed class
private override void M() { ........ }
// Is this method automatically sealed? In the meaning that it doesn't have to look in VTable and can be called directly?
// Also what about O() can it be called directly too, without VTable?
}
"I guess I can make this private for sealed class"
You can't change access modifier in inheritance hierarchy. It means if method is public
in base class you can't make it private
or internal
or protected
in derived classes. You can change modifier only if you declare your method as new
:
private new void M() { ........ }
As far as I know making class sealed gets rid of look up in VTable or am I wrong?
Sealed class is last in hierarchy because you can't inherit from it. Virtual table can be used with sealed
class in case this sealed
class overrides some method from base class.
If I make a class sealed does this mean that all virtual methods in class hierarchy are also marked sealed?
You can see IL code:
.method family hidebysig virtual // method is not marked as sealed
instance void M () cil managed
{
.maxstack 8
IL_0000: nop
IL_0001: ret value
}
Method is not marked as sealed
. Even if you explicitly mark this method as sealed
you will get the same IL code.
Also, there is no reason to mark method as sealed
in sealed
class. If class is sealed
you can't inherit it so, you can't inherit it's methods.
About virtual tables - if method is overriding and you delete it from virtual table you can never use it in inheritance hierarchy so, there is no reason to override method and never use it in inheritance hierarchy.
The first thing to note is that C# generally makes virtual calls to any instance method on a reference type, even when the method isn't virtual. This is because there is a C# rule that it's illegal to call a method on a null reference that isn't a .NET rule (in raw CIL if you call a method on a null reference and that method doesn't itself access a field or virtual method, it works fine) and using callvirt
is a cheap way to enforce that rule.
C# will generate call
rather than callvirt
for non-virtual instance calls in some cases where it is obvious that the reference isn't null. In particular with obj?.SomeMethod()
since the ?.
means a null-check is already happening, then if SomeMethod()
isn't virtual this will be compiled to call
. This only happens with ?.
since the code for compiling ?.
can do that check whereas it doesn't happen with if (obj != null){obj.SomeMethod();}
because the code compiling .
doesn't know it is following a null-check. The logic involved is very localised.
It is possible at the CIL level to skip a virtual table lookup for virtual methods. This is how base
calls work; compiled to a call
on the base's implementation of a method rather than a callvirt
. By extension in the construct obj?.SomeMethod()
where SomeMethod
was virtual and sealed (whether individually or because the type of obj
was sealed) then it would be theoretically possible to compile that as a call
to the most derived type's implementation. There are though some extra checks that need to be made in particular to make sure it still works correctly if classes in the hierarchy between the declaring type and the sealed type add or remove overrides. It would require some global knowledge of the hierarchy (and assurance that knowledge won't change, which means all the types being in the assembly currently being compiled) for the optimisation to be safe. And the gain is tiny. And still not available most of the time for the same reason that callvirt
is used even on non-virtual calls most of the time.
I don't think there's anywhere where sealed
affects how the compiler generates the call, and it certainly isn't affecting it most of the time.
The jitter is free to apply more knowledge though, but again the difference if any is going to very small. I'd certainly recommend marking classes you know won't be overridden as sealed
and if the jitter makes use of that then that's great, but the main reason I'd recommend it isn't performance but rather correctness. If you somewhere try to override a class that you had marked sealed
then either A. You've just changed the design a bit and knew you'd have to remove the sealed
(.5seconds work to remove it) or B. You've done something in one place you were sure you wouldn't in another. It's good to have the brief pause of reconsidering.
If I make a class sealed does this mean that all virtual methods in class hierarchy are also marked sealed?
They are considered sealed the same as if explicitly marked as such.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With