NEWS

Tuesday, July 19, 2011

Sealed classes in .NET …

While tinkering with C#, I was experimenting with declaring methods “virtual.” This effectively tells subclasses “you can override this method with your own implementation.” After some more tinkering, it seems you can also hide methods that are not declared virtual by prefixing them with “new.” But there is a difference:




using System;



public class A {

public void method() {

Console.WriteLine("A.method");

}

public virtual void method2() {

Console.WriteLine("A.method2");

}

}



public class B : A {

new public void method() {

Console.WriteLine("B.method");

}

public override void method2() {

Console.WriteLine("B.method2");

}

}



The compiler will not complain about this situation. Now pretend that we execute this: B b = new B(); b.method(); b.method2();. We get the output we expect:



B.method

B.method2



Now let’s try A b = new B(); b.method(); b.method2();. See the difference? This time we store the B in an A reference.



A.method

B.method2



Java users will be shocked at this behaviour. But B.method overrides A.method, right?



Wrong. It hides A.method. If we had said A b = new B(); (b as B).method(); we would get the output we expect. This behavior comes directly from C++: if a non-virtual method is “overridden” then which method gets called depends on the reference type used to call it!



This serves two purposes:



  1. If a method is not declared virtual, the CLR doesn’t have to waste time looking for overridden methods on child classes; it can jump directly to the method.
  2. The virtual modifier can be thought of as giving permission to override. If you target a non-virtual method you know it will behave the same way, so long as you are using the same reference type when invoking it.



Which brings me to the question of the day. C# (and any .NET language) supports the notion of a “sealed” class — one that cannot be derived from at all. Immutable classes, such as System.String, are sealed to prevent tampering that could aggravate developers or compromise the entire .NET security architecture.



But what if System.String weren’t sealed? Why, nothing! Almost. Methods overloaded from Object (such as ToString) are implicitly virtual — but if they were marked “sealed” in System.String they are effectively “un-virtualed!” Redefining ToString in a subclass would require “new” and would exhibit the same behavior as the example above. For example:



using System;



public class A {

public virtual void Foo() {

Console.WriteLine("A");

}

}



public class B : A {

public sealed override void Foo() {

Console.WriteLine("B");

}

}



public class C : B {

new public void Foo() {

Console.WriteLine("C");

}

}



public class R {

public static void Main() {

C c = new C();

B b = c;

A a = b;



a.Foo();

b.Foo();

c.Foo();

}

}



This outputs the expected:



B

B

C



The benefit of un-sealing these classes is that we could subclass System.String to add our own properties, be able to pass this object around as if it were a string, and not sacrifice the integrity of its immutability. As long as none of System.String’s immutable information is protected or public, we can’t touch it.



Where the system treats it as a string, it is a string. Where we treat it as a subclass, we can extend its behavior how we like.


The concept of "Hiding" in c# is the same as the concept of "Shadowing" in vb.net

No comments:

Post a Comment