Why C# Implements Methods as Non-Virtual by Default

Why C# implements methods as non-virtual by default?

Classes should be designed for inheritance to be able to take advantage of it. Having methods virtual by default means that every function in the class can be plugged out and replaced by another, which is not really a good thing. Many people even believe that classes should have been sealed by default.

virtual methods can also have a slight performance implication. This is not likely to be the primary reason, however.

Why are C# interface methods not declared abstract or virtual?

For the interface, the addition of the abstract, or even the public keywords would be redundant, so you omit them:

interface MyInterface {
void Method();
}

In the CIL, the method is marked virtual and abstract.

(Note that Java allows interface members to be declared public abstract).

For the implementing class, there are some options:

Non-overridable: In C# the class doesn't declare the method as virtual. That means that it cannot be overridden in a derived class (only hidden). In the CIL the method is still virtual (but sealed) because it must support polymorphism regarding the interface type.

class MyClass : MyInterface {
public void Method() {}
}

Overridable: Both in C# and in the CIL the method is virtual. It participates in polymorphic dispatch and it can be overridden.

class MyClass : MyInterface {
public virtual void Method() {}
}

Explicit: This is a way for a class to implement an interface but not provide the interface methods in the public interface of the class itself. In the CIL the method will be private (!) but it will still be callable from outside the class from a reference to the corresponding interface type. Explicit implementations are also non-overridable. This is possible because there's a CIL directive (.override) that will link the private method to the corresponding interface method that it's implementing.

[C#]

class MyClass : MyInterface {
void MyInterface.Method() {}
}

[CIL]

.method private hidebysig newslot virtual final instance void MyInterface.Method() cil managed
{
.override MyInterface::Method
}

In VB.NET, you can even alias the interface method name in the implementing class.

[VB.NET]

Public Class MyClass
Implements MyInterface
Public Sub AliasedMethod() Implements MyInterface.Method
End Sub
End Class

[CIL]

.method public newslot virtual final instance void AliasedMethod() cil managed
{
.override MyInterface::Method
}

Now, consider this weird case:

interface MyInterface {
void Method();
}
class Base {
public void Method();
}
class Derived : Base, MyInterface { }

If Base and Derived are declared in the same assembly, the compiler will make Base::Method virtual and sealed (in the CIL), even though Base doesn't implement the interface.

If Base and Derived are in different assemblies, when compiling the Derived assembly, the compiler won't change the other assembly, so it will introduce a member in Derived that will be an explicit implementation for MyInterface::Method that will just delegate the call to Base::Method.

So you see, every interface method implementation must support polymorphic behavior, and thus must be marked virtual on the CIL, even if the compiler must go through hoops to do it.

Why methods in C# are not automatically virtual?

You should care which members can be overridden in derived classes.

Deciding which methods to make virtual should be a deliberate, well-thought-out decision - not something that happens automatically - the same as any other decisions regarding the public surface of your API.

Are methods virtual by default or not virtual?:

well, you got it right. If it's not virtual, it gets hidden.

The new keyword brakes the virtual overriding in the inheritance hierarchy chain.

Simple example to read: Polymorphism, Method Hiding and Overriding in C#

Why are C# classes unsealed by default, when their methods are non-virtual by default?

I believe the question is "given that there are good reasons why methods should be non-virtual by default, why are classes not also sealed by default?"

One could also add: C# makes the default accessibility either internal, for top-level types, or private, for members of types; that is, it chooses the more restrictive and safer option; if a developer wishes a less restrictive, more dangerous option they can opt into it. Sealing by default would also be choosing the more restrictive and safer option as the default.

Also: unsealing a class is never a breaking change, but deciding later that you wish you'd made a class sealed and sealing it is a breaking change. C# typically prefers the design choice that encourages fewer breaking changes, so for this reason also, you would think that C# classes should be sealed by default.

We've identified three reasons why sealed by default is both a good idea and consistent with other design choices in C#. Why then is C# inconsistent in this regard, choosing to make unsealed classes the default?

I don't know. It's always struck me as a minor design flaw in C#. I've never seen a cogent argument for this choice, and I do not know if it was debated in early design meetings or not; that was before my time on the design team.

Unless you run into someone who was in that design meeting in 2001 and ask them, you might not get a satisfactory answer to your question.

I am in the habit of sealing every class I write unless I have a reason to design it for inheritance; I encourage everyone to do the same.

Why not make everything 'virtual'?

Eric Lippert covers this here, on method hiding

C# call an interface method non-virtual implementation

Because AAble is implementing the IAble interface, its AAble.f is marked as the implementation of the IAble.f method for type AAble.

BAble.f is simply hiding the AAble.f method, it is not overriding it.

IAble o = new BAble(); o.f(); // calls AAble.f
AAble o = new BAble(); o.f(); // calls AAble.f
BAble o = new BAble(); o.f(); // calls BAble.f
IAble o = new CAble(); o.f(); // calls CAble.f

The decision is made at compile-time:

// AAble.f in IL:
.method public final hidebysig newslot virtual
instance void f () cil managed

// BAble.f in IL:
.method public hidebysig
instance void f () cil managed

Interface implementations are marked as virtual in IL, even though it wasn't marked virtual in C#. The method is also marked as final in IL, if the method would've been virtual in C#, it would not have been marked as final.

Why doesn't C# have a non-virtual calling convention?

This might explain it: http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx

Shortly: call instruction can accept null as this pointer (as it is in C++). This is errorsome and C# team decided to use callvirt wherever it is possible so that calls on null pointers throw NullReferenceException



Related Topics



Leave a reply



Submit