C# Canonical Forms—NVI Pattern
Use the NVI Pattern
Many times, when you design a class specifically capable of acting as a base class in a hierarchy, you often declare methods that are virtual so that deriving classes can modify the behavior. A first pass at such a base class may look something like the following:
using System; public class Base { public virtual void DoWork() { Console.WriteLine( "Base.DoWork()" ); } } public class Derived : Base { public override void DoWork() { Console.WriteLine( "Derived.DoWork()" ); } } public class EntryPoint { static void Main() { Base b = new Derived(); b.DoWork(); } }
Not surprisingly, the output from the previous example looks like this:
Derived.DoWork()
However, the design could be subtly more robust. Imagine that you’re the writer of Base, and
you have deployed Base to millions of users. Many people are happily using Base all over the world
when you decide, for some good reason, that you should do some pre- and postprocessing within
DoWork(). For example, suppose you would like to provide a debug version of Base that tracks how
many times the DoWork() method is called. As written previously, you cannot do such a thing without
forcing breaking changes onto the millions of users who have used your class Base. For example,
you could introduce two more methods, named PreDoWork and PostDoWork, and ask kindly that your
users reimplement their overrides so that they call these methods at the correct time. Ouch! Now,
let’s consider a minor modification to the original design that doesn’t even change the public interface
of Base:
using System; public class Base { public void DoWork() { CoreDoWork(); } protected virtual void CoreDoWork() { Console.WriteLine( "Base.DoWork()" ); } } public class Derived : Base { protected override void CoreDoWork() { Console.WriteLine( "Derived.DoWork()" ); } } public class EntryPoint { static void Main() { Base b = new Derived(); b.DoWork(); } }
This nifty little pattern is called the Non-Virtual Interface (NVI) pattern, and it does exactly
that: It makes the public interface to the base class nonvirtual, but the overrideable behavior is
moved into another protected method named CoreDoWork. The NVI pattern is similar to the Template
Method pattern as described by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides in
Design Patterns: Elements of Reusable Object-Oriented Software (Boston, MA: Addison-Wesley
Professional, 1995). .NET Framework libraries use the NVI pattern widely, and it’s circulated in library
design guidelines at Microsoft for obviously good reasons. In order to add some metering to the
DoWork method, you only need to modify Base and the assembly that contains it. Any of the other
classes that derive from assembly don’t even have to change.
Another technique that typically gets used with NVI in the C++ world is that of actually declaring
the virtual method private, as in the following code that unfortunately won’t compile in C# for
reasons I’ll explain shortly:
// WILL NOT COMPILE!!!!! using System; public class Base { public void DoWork() { CoreDoWork(); } // WILL NOT COMPILE!!!!! private virtual void CoreDoWork() { Console.WriteLine( "Base.DoWork()" ); } } public class Derived : Base { // WILL NOT COMPILE!!!!! private override void CoreDoWork() { Console.WriteLine( "Derived.DoWork()" ); } } public class EntryPoint { static void Main() { Base b = new Derived(); b.DoWork(); } }
This code would actually compile in the initial .NET 1.0 release of C#, and the technique was perfectly valid in the CLR based upon the fact that the CLI spec at the time and C# wanted to match the C++ semantics as closely as possible. Before I explain why this won’t work in C# now, let me explain why you would want to do it in the first place.
There is a fundamental difference between a method’s visibility and its accessibility. If the method is in the declaration of a class or struct, no matter what its protection level, it is visible. And traditionally, in order for a derived class to override a method, it merely has to be visible, and not accessible.
Note The only thing private means on a C++ private virtual method is that the derived class may not call the base class’s implementation. If you don’t believe me, try this example in native C++. You’ll find that it works as expected.
The beauty of being able to declare private virtual methods is that you don’t have to worry
about derived classes misusing your Base class. For example, maybe you require that they don’t call
your base class implementation of the virtual method. Fine, just make it private virtual. In fact,
using such a technique, you should only make your method protected virtual if you know there
is a good reason why the base class would need to call it. And if you do that, you must strictly document
at what point the override should call the base implementation. Many people believe that just
because the method is declared private, it cannot be overridden. But in the strict sense, restricted
accessibility doesn’t make it invisible to overriding.
Now let me explain why this feature was turned off in the .NET 1.1 release of C#. This was amystery
to me until Brandon Bray from the Microsoft Visual C++ team explained it neatly. The fact that
you can inherit across assembly boundaries turns this feature into a sort of security hole. With native
C++, it was never an issue. Consider this: If a private virtual method can be overridden, then so
can an internal virtual method. And therein lies the problem. It would allow you to override the
behavior of an internal virtual method on some random class in a particular assembly, and that is
the source of the security hole. So, a tradeoff was made, and every release starting with the 1.1 release
has this feature disabled. Incidentally, this same fix was added to C++/CLI. Although native C++ classes
can use the private virtual method feature effectively, ref C++ classes cannot. And that, of course,
is due to the fact that ref C++ classes represent .NET ref types that can be inherited across assembly
boundaries. How about that!
|

