#681 – Avoid Using the new Keyword To Hide Methods

You can use the new keyword in a derived class to hide a method in a base class, rather than overriding it.  In other words, the method in the derived class is not polymorphic, so if the method is called from a variable whose type matches the base class, the method in the base class will be called, rather than the method in the subclass.

// In Dog - public virtual void Bark()
// In Terrier : Dog - public new void Bark()

Dog d = new Terrier("Bob");
d.Bark();   // Dog.Bark called, rather than Terrier.Bark

If a Terrier really is-a type of Dog, then this lack of polymorphism on the Bark method doesn’t really make sense.  If you wanted to add a method to Terrier that was really something entirely new, you should give it a new name to avoid confusion, rather than using the new keyword.

#680 – Virtual Modifier Combinations

Below is a chart showing all of the possible combinations for the virtual, override, new and sealed modifiers, which dictate polymorphic behavior of inherited members.

The chart shows the allowed combinations for an inheritance tree consisting of three classes–DogTerrier (which inherits from Dog) and Yorkshire (which inherits from Terrier).  It’s assumed that there is a member in each class, e.g. a Bark method.  This chart then shows all the possible combinations of modifiers for that member, within each of the three classes.

For example, you could:

  • Declare Dog.Bark as virtual
  • Declare Terrier.Bark as override
  • Declare Yorkshire.Bark as new

#679 – Hide An Inherited Method with A Virtual Method

You typically hide an inherited method using the new keyword, indicating that the method should not behave polymorphically.  If you later inherit this method in a subclass, you won’t be able to override the method marked as new.

For example:

  • virtual Dog.Bark  – can override
  • new Terrier.Bark – hides Dog.Bark
  • new Yorkshire.Bark – must hide, can’t override

However, if you want to hide an inherited method, but still allow overriding in a subclass, you can use the virtual new combination:

  • virtual Dog.Bark – can override
  • virtual new Terrier.Bark – hides Dog.Bark, but allows overriding
  • override Yorkshire.Bark – overrides Terrier.Bark

In this case, Terrier and Yorkshire objects referenced by variables of type Dog will not behave polymorphically.  But a Yorkshire object referenced by a Terrier variable will behave polymorphically.

 

#678 – A Sealed Method Cannot Be Overridden

When you override a method, you create a virtual method that behaves polymorphically.  The base class defines the method using the virtual keyword and you define the method in your child class using the override keyword.

When you use the override keyword, your method can itself be overridden in a child class.

If you want to override a method in a base class, but also prevent any derived classes from overriding your method, you can use the sealed keyword to indicate that a child class must use the new keyword, rather than overriding your method.

    public class Dog
    {
        public virtual void Bark()
        {
            Console.WriteLine("  Dog.Bark");
        }
    }

    public class Terrier : Dog
    {
        public sealed override void Bark()
        {
            Console.WriteLine("  Terrier is barking");
        }
    }

    public class JRT : Terrier
    {
        public new void Bark()
        {
            Console.WriteLine("  JRT is barking");
        }
    }

#677 – Method Marked with new Modifier Cannot Be Overridden

You can use the new modifier on a method to indicate that the method should hide a method of the same name in a base class.  In other words, the method does not behave polymorphically.

If you’ve marked a method as new in a class, that method is not virtual and can not be overridden in a derived class.

In the example below, the Dog class marks Bark as virtual, indicating that it can be overridden.  The Terrier class marks Bark as new, indicating that it does not override Dog.Bark–and that it cannot be overridden.  JRT can therefore not override Bark.

    public class Dog
    {
        public virtual void Bark()
        {
            Console.WriteLine("  Dog.Bark");
        }
    }

    public class Terrier : Dog
    {
        public new void Bark()
        {
            Console.WriteLine("  Terrier is barking");
        }
    }

    public class JRT : Terrier
    {
        public new void Bark()
        {
            Console.WriteLine("  JRT is barking");
        }
    }

#676 – An Overridden Method Can Itself Be Overridden

To get polymorphic behavior for a method in a derived class, you need to mark the method in the base class as virtual and the method in the derived class as override.  This allows the method in the derived class to be called at runtime, even when referenced by a variable whose type is the base class.

    public class Dog
    {
        public virtual void Bark()
        {
            Console.WriteLine("  Dog.Bark");
        }
    }

    public class Terrier : Dog
    {
        public override void Bark()
        {
            Console.WriteLine("  Terrier is barking");
        }
    }

The Bark method in Terrier overrides the version in Dog.  But the override method also indicates that the Bark method can itself be overridden in a class that derives from Terrier.

    public class JRT : Terrier
    {
        public override void Bark()
        {
            Console.WriteLine("  JRT is barking");
        }
    }

The Bark method is now polymorphic across all three types.

#675 – Polymorphic Behavior Requires virtual / override Combination

Recall that polymorphism means that the type of an object at run-time is used to decide what method to call, rather than the static type of the variable that references that object.

In C#, a method behaves polymorphically if and only if the method in the base class is defined as virtual and the method in the derived class is defined as override.  

For example, if Terrier inherits from Dog, we can declare a Dog.Bark method as virtual and a Terrier.Bark method as override.  We then get polymorphic behavior:

Terrier t = new Terrier("Jack");
Dog d = t;

// Polymorphic behavior =
//   Terrier's implementation of Bark is called,
//   because type of object referenced by d is
//   determined at runtime.
d.Bark();

#674 – Can Overload If Parameters Differ Only by ref Modifier

You can overload methods, i.e. create two methods with the same name, if the methods have different parameter lists, where the lists differ in the number or type of the parameters.  For example:

        public void Fetch(Bone b)
        {
            Console.WriteLine(string.Format("Fetching bone {0}", b.Description));
        }

        public void Fetch(Dog d)
        {
            Console.WriteLine(string.Format("I'll go get {0}", d.Name));
        }

You can also overload methods if they differ only in their use of the ref modifier.  One method can accept a parameter passed by value and you can have another version of the method that allows passing the parameter by reference.

        public void Bark(string sound)
        {
            Console.WriteLine(sound);
        }

        public void Bark(ref string sound)
        {
            Console.WriteLine(sound);

            // Update caller's copy of sound variable
            sound = sound + "!";
        }
            Dog d = new Dog("Rex", 3);
            string sound = "woof";
            d.Bark(sound);
            d.Bark(ref sound);   // woof!
            d.Bark(ref sound);   // woof!!

#673 – Types Used in using Statement Must Implement IDisposable

The using statement is a shortcut for a try/finally block where the Dispose method is automatically called on any objects instantiated as part of the using statement.

            using (StreamWriter sw = new StreamWriter(@"D:\Hi.txt"))
            {
                sw.Write("Hi !");
            }
            // StreamWriter.Dispose automatically called

Because the Dispose method is called automatically on all objects instantiated as part of the using statement, each object must belong to a type that implements IDisposable.  The example below leads to a compile-time error because Dog does not implement IDisposable and therefore does not have a Dispose method.

            using (Dog d = new Dog("Kirby", 15))
            {
                d.Bark();
            }

#672 – An Exception Thrown From a Finalizer Will Be Treated as an Unhandled Exception

If an exception originates in a finalizer in C# and is not handled within the body of the finalizer, the exception will be treated by the application as an unhandled exception.  If you have no mechanism for dealing with unhandled exceptions, this will lead to a crash.

In the example below, the exception originating in the Dog finalizer is not caught within the Main method.

        static void Main()
        {
            try
            {
                Dog d = new Dog();
                d = null;

                GC.Collect();
                GC.WaitForPendingFinalizers();

                Console.WriteLine("I made it to the end of the program");
                Console.ReadLine();
            }
            catch (Exception xx)
            {
                Console.WriteLine(string.Format("Hey, I caught an exception! {0}", xx.ToString()));
            }
    public class Dog
    {
        public Dog()
        {
            Console.WriteLine("Dog constructor");
        }

        ~Dog()
        {
            Console.WriteLine("Dog finalizer running");
            throw new Exception("** Exception from Dog finalizer **");
        }
    }