Search
Close this search box.

C# Fundamentals: Beware Of Implicit Hiding

C# Fundamentals Genesis

I’ve decided to begin a line of back-to-basics blogs that I hope to post regularly.  I used to teach C++ Programming, Algorithms, and Data Structures courses at Webster University in the evenings here locally, and have missed those teaching and mentoring moments when I had to stop teaching after the birth of my twins (no more free time for me!). 

While some of these fundamentals will be obvious to those who know the languages well (I hope to post C# and C++ tidbits), I’m hoping these observations will be of use to those who are newer to our industry and help prevent potential mistakes and/or give them a better understanding of the language. 

Beware of Implicit Hiding

I wanted to start off with a problem that I’ve seen bite many developers over the year because it’s one of those areas where C#’s default behavior differs substantially from that of its predecessors (C++ and Java).

Let me illustrate with a quick example.  Let’s say that you’re building a messaging library to abstract your messaging provider implementation away from your projects.  So in your code-base, you create an AbstractMessageConsumer that contains the basic message consumer functionality, and then inheriting from that you create a TopicMessageConsumer which adds to that the specifics for consuming from a pub/sub topic.

Now, obviously these type of classes are each going to contain some core resources that need to be managed and cleaned up (like connections to the underlying message source).  So you decide to follow the IDisposable paradigm and implement that interface.  Perhaps you do something like this:

public abstract class AbstractMessageConsumer : IDisposable {
  // ...

  public void Dispose() {
    Console.WriteLine("Disposing the AbstractMessageConsumer.");
  }
}

public sealed class TopicMessageConsumer : AbstractMessageConsumer {
  // ...

  public void Dispose() {
    Console.WriteLine("Disposing a Topic Message Consumer.");
    base.Dispose();
  }
}

On the surface, this seems reasonable, and if you aren’t watching your warnings list and just see the “build succeeded” message in the status bar, you may think all is well.  But let’s look at a program to generate some output:

public static class Program {
  public static void Main() {
    TopicMessageConsumer topicConsumer = new TopicMessageConsumer();

    AbstractMessageConsumer abstractConsumer = topicConsumer;

    IDisposable disposable = topicConsumer;

    Console.WriteLine("From topicConsumer:");
    topicConsumer.Dispose();

    Console.WriteLine("\nFrom abstractConsumer:");
    abstractConsumer.Dispose();

    Console.WriteLine("\nFrom IDisposable:");
    disposable.Dispose();
  }
}

So, I create an instance of the subclass (topicConsumer) and call dispose on it 3 ways: through a TopicMessageConsumer reference, an AbstractMessageConsumer reference, and a IDisposable reference.  Guess what the output is?  You may think that in all cases it would call Dispose() on the sub-class.  But you’d only be right in one case out of three:

    From topicConsumer:
    Disposing a Topic Message Consumer.
    Disposing the AbstractMessageConsumer.
     
    From abstractConsumer:
    Disposing the AbstractMessageConsumer.
     
    From IDisposable:
    Disposing the AbstractMessageConsumer.

What happened here?  We have fallen a victim to implicit hiding.  When a sub-class hides a method from its base-class, that means that it provides a new definition that will be used when the method is called from that reference.  In short, calling from the base-class reference – abstractConsumer.Dispose() – will call AbstractMessageConsumer.Dispose() declared in the base class.  But calling from the sub-class reference – topicConsumer.Dispose() – will call TopicMessageConsumer.Dispose() declared in the sub class.

Two things have worked against us here:

  1. When you implement from an interface, whether the method is virtual or not depends entirely on the implementing class.
  2. When you attempt to override a method in a sub-class, if you do not explicitly specify the “override” keyword, it will hide the base-class method instead.

The first point differs substantially from Java’s interfaces (because in Java everything is a virtual call) and the typical C++ equivalent (C++ doesn’t have interfaces, per se, but are generally implemented as abstract classes with all pure-virtual methods).

Thus, if you intend to implement an interface and want your sub-classes to be able to supply a new implementation (or add something to it), you should declare it to be virtual or abstract (remember, an abstract method is a virtual method with no definition, you are deferring it’s definition to the sub-class).

Thus this is one step closer to correct:

public abstract class AbstractMessageConsumer : IDisposable {
  // ...

  // now virtual, this means that if our sub-classes override us, calls to us
  // will go to their definition instead.
  public virtual void Dispose() {
    Console.WriteLine("Disposing the AbstractMessageConsumer.");
  }
}

Notice, I said one step closer to correct.  What does that mean?  Well….  Unlike C++, declaring the base method as virtual is not enough.  In C++, once you declare a method to be virtual, any sub-classes that have the same method will automatically override it.  However, in C# this is not true because the default in C# is to hide and not to override the method.  Thus even with our virtual base class definition of Dispose(), if we don’t correct the sub-class to override, we are still hiding the base-class definition of Dispose().

Look at the following three definitions and their meanings.

// implicitly hides any definition of Dispose() in the base-class.
public void Dispose() {
  ...
}

// explicitly hides any definition of Dispose() in the base-class.
public new void Dispose() { ... }

// explicitly overrides the original definition of Dispose() in the base class.
public override void Dispose() { ... }

So you see, the only way to get the override behavior we desire is to explicitly use the override keyword.  In my mind, this is backwards.  I would have rather had the default be to override implicitly and force hiding to be explicit since hiding has more negative potential consequences than overriding does.  That said, this is the world we live in, so if we want this to work right, we must declare it to be an override:

public sealed class TopicMessageConsumer : AbstractMessageConsumer {
  // ...

  public override void Dispose() {
    Console.WriteLine("Disposing a Topic Message Consumer.");
    base.Dispose();
  }
}

Now we get the results we expect from all three calls:

From topicConsumer
    : Disposing a Topic Message Consumer.Disposing the AbstractMessageConsumer
          .

      From abstractConsumer
    : Disposing a Topic Message Consumer.Disposing the AbstractMessageConsumer
          .

      From IDisposable
    : Disposing a Topic Message Consumer.Disposing the AbstractMessageConsumer.

So how can we protect ourselves from this pitfall?  You should always watch your warnings!  As they say, a warning is an error waiting to happen.  Fortunately, C# doesn’t tend to give you near as many warnings as C++, so it’s easy to get in the habit of obeying and correcting all warnings.  If you really want motivation, go into your visual studio project settings and enable “Treat warnings as errors” on the Build tab. 

Hiding has its uses, but until you get very comfortable in knowing when it is appropriate to hide vs. override, I recommend the following:

  • Consider enabling warnings-as-errors build option so that warnings about hiding will fail at compile time.
  • When implementing an interface:
    • If you want to subclass, make definitions virtual.
    • If you do not want to subclass, seal your class.
  • Always avoid the implicit hide behavior – make your override or hide explicit.
  • Prefer to never hide a method unless there is a very compelling reason to do so

Technorati Tags: .NET,C#,Software,Development

This article is part of the GWB Archives. Original Author:  James Michael Hare

Related Posts