tags:

views:

459

answers:

7

This question really is kinda pointless, but I'm just curious:

This:

public sealed class MyClass
{
   protected void MyMethod(){}
}

compiles, but gives a warning

while This:

public sealed class MyClass
{
   public virtual void MyMethod(){}
}

doesn't compile. Just out of sheer curiosity, is there a reason for this?

+10  A: 

virtual is used to declare a method/property "override-able".

sealed is used to declare that class cannot be inherited from.

So a virtual method in a sealed class could never be overridden, as the class could never be inherited from. It just doesn't make sense.

protected affects access to a member, it does not declare it "override-able" as virtual does (though it is often used in that manner) and is accordingly not contradictory.

Kevin Montrose
Yes, I understand that. Notice I didn't ask "why doesn't this compile" rather I asked why does this compile. The same way virtual doesn't make sense in a sealed class, protected doesn't either see what I'm saying?
BFree
I *think* the OP realizes that `sealed` doesn't make particular sense with `virtual` or with `protected`. He's wondering why one "not making sense" item is allowed, while another is not.
280Z28
@280Z28 I agree... We need Skeeter to explain this to us.
Jason Down
It's around 3AM in the UK, so Jon is most likely asleep. You'll have to wait until dawn...
Martinho Fernandes
Not only does the CLI allow sealed types to declare new virtual members, but it allows calling non-virtual methods with the `callvirt` IL instruction. Everything we talk about here are C# "limiting" things where it makes sense to, as the underlying byte code isn't enforcing it.
280Z28
Not only does it not make sense, it is also impossible. Sealed explicitly prevents the overhead of a virtual function call (MSIL 'call' vs. 'callvirt').
cfeduke
@cfeduke: `sealed` means the compiler *can* use `call`, it doesn't mean it has to.
280Z28
The CLI is not responsible for enforcing language rules. You could handwrite CIL if you wanted.
Kevin Montrose
@Kevin: The CLI enforces many things about virtual methods. For example, they can't be static, and they can't be constructors. Also, abstract and sealed methods *must* be virtual.
280Z28
@280Z28: Yes, but that's not enforcing C# rules. Things are conflated a bit because C#/CLI have evolved together, but just because C# is enforcing something does not mean the CLI does. That's what I'm trying to convey with my prior comment.
Kevin Montrose
protected also controls the access from outside the class. Granted, you could get the same behavior by making the ctor private (which is why you get the warning), but the main point is it actually makes a difference. virtual has no other effect than directly contradicting sealed.
Tal Pressman
+2  A: 

I can't see a good reason for this. The protected MyMethod can be called from MyClass, but will never be called from a derived class (because MyClass is sealed). The virtual version is also allowed to be called directly from MyClass, but it is illegal for the method to have an override because you can't derive a class from MyClass...

Jason Down
+2  A: 

A sealed class can have protected members via inheritance. When a method is part of a class, it doesn't matter how that method got there.

In the first case, with the protected method on the sealed class, its the same as if the sealed class inherited a protected method. So it compiles.

Out of curiosity, what exactly is the warning given?

abelenky
The warning is: "warning CS0628: 'SimpleTestApp.Class1.foo()': new protected member declared in sealed class."
Jason D
+1  A: 

A sealed class cannot be sub-classed, therefore virtual is not an option. Thus error.

This first is a bit silly but valid, thus warning.

Simeon Pilgrim
+2  A: 

The error is:

CS0549: 'function' is a new virtual member in sealed class 'class'.

First of all, despite the fact that it doesn't really make sense to include new protected or virtual members in a sealed class, the CLI¹ does allow it. The CLI also allows calling members of a sealed class using the callvirt IL instruction, even though a compiler could freely replace it with the call instruction.

Currently, I can't find anything in ECMA-334 (C# Language Specification) that requires the compiler emit the above error. It appears like a Microsoft's implementation added the error just because it doesn't make sense to include new virtual members in a sealed class.

¹The CLI is a virtual machine, and the C# compiler emits byte code that runs on it. Almost any concept that's illegal in the CLI is also illegal in C# for that reason - but this is a case where C# does a little extra (not that it's a problem).

Edit: It seems to posts getting marked up are explaining why it doesn't make sense to write code like that in the OP. But regarding what rule made it a compiler error they appear to be wrong.

280Z28
A: 

I'd guess the compiler does some optimizations with sealed classes that are impossible if you have a virtual method declared - "not having a vtable" seems a likely candidate.

That's just a guess, though.

kyoryu
+3  A: 

The only reason I can think of is that sometimes you would need to write protected methods to override other protected methods. The language could have been designed to allow this:

protected override void Foo()

but not this

protected void Foo()

but that might have been seen to be a little hard to follow - it's the absence of override which makes it useless, whereas in the case of

public virtual void Foo()

it's the presence of virtual that is useless. The presence of something "wrong" is probably easier to understand than the absence of something useful.

In this case, being virtual may also have performance implications, whereas making something protected instead of private probably doesn't - so it's a bit more severe.

These are just guesses though really - if we're really lucky, Eric Lippert will give a more definitive answer. He's the one you want, not me :)

Best answer: treat warnings as errors and they're equivalent anyway ;)

Jon Skeet
Ooh, good point, I didn't think of the case where the sealed class in question is inheriting from a base and overriding a protected member. That could very well be a reason. I actually emailed Eric last night, and he responded saying he'll look into it and get back to me. I'll keep this question open till he does. Thanks Jon.
BFree
Hey, I'm just guessing too, guys. The language design notes document that the decision to make introducing a virtual method into a sealed type an error was made on the 18th of October, 1999, but does not give any justification for the decision. I can find nowhere in the notes that justifies why introducing a new protected member ought to be legal. My best guess: this was probably simply an oversight in the first version, and then it became a breaking change to ever fix it.
Eric Lippert
@Eric: Out of interest, where is this in the spec? It doesn't appear to be in the introductory part of 10.6 which lists the rules for valid combinations of modifiers. I thought I'd find the rule and then see if it's mentioned in either of the annotated specs... and now I can't find the rule. I can't see it in 10.6.3 either.
Jon Skeet
Um.... I can't find it either. Clearly this was intended to go into the spec, the rule is right there in the notes. I'll add yet another point to our long list of omissions from the spec. Thanks for bringing it to my attention.
Eric Lippert
@Eric, could this be a matter of implementation challenges influencing design decisions? It does seem a bit odd as the base classes of a sealed class can have virtual methods, yet the sealed class itself cannot. With that said it would need to ensure all virtual methods are filled in...*(But, I do concede that it's a bit silly looking to read virtual or protected within a sealed class.)*
Jason D