Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C# Virtual Functions coming from C++ Warning !!

0.00/5 (No votes)
4 Jan 2017 1  
When trying to move into C# from C++, a little explanation of how functions can be overridden, and some of the pitfalls you may encounter.

Introduction

When I moved over to C# years ago, I had been programming predominantly in C++. I was immediately frustrated at getting virtual functions working! Yes the very functionality in the language that supports polymorphism, late binding!

Background

So what is polymorphism. You have a base type pointer or reference, and it is not immediately statically fixed to the type it has to be forever. In other words, take a reference or pointer to a base type of some kind, and make it point/refer to an instance of a derived class in the inheritance hierarchy.

C++ - Pointer

Base *basePtr = new Derived();

C# Reference

Base b = new Derived();

Remember in C#, references are the preferred vessel to hold your instances, you can use pointers but in general it's not needed.

Using the Code

So we want to use late binding, or call functions on the type that is instantiated later when the program is running, we don't know what type it will be now and therefore we have dynamic binding.

So we have a type, instantiate a derived one and call some functions. Easy. But look what happens until I discovered the word overridden! Now you must appreciate I came from C++, so here is what I saw:

using System;

namespace ConsoleApplication2
{
    public class baser
    {
        public void hello()
        {
            Console.WriteLine("hello on base instance - non virtual on base");
            Console.WriteLine("func is - non virtual on deriv");
        }

        public virtual void hello2()
        {
            Console.WriteLine("hello2 on base instance - virtual on base");
            Console.WriteLine("func is  - virtual on deriv");
        }

        public void hello3()
        {
            Console.WriteLine("hello3 on base instance - non virtual on base");
            Console.WriteLine("func is new and virtual on deriv");
        }

        public virtual void hello4()
        {
            Console.WriteLine("hello4 on base instance - virtual on base");
            Console.WriteLine("func is new and virtual on deriv");
        }

        public virtual void hello5()
        {
            Console.WriteLine("hello5 on base instance - virtual on base");
            Console.WriteLine("func is override on deriv");
        }
    }

    public class deriv : baser
    {
        public void hello()
        {
            Console.WriteLine("hello on deriv - non virtual on deriv");
            Console.WriteLine("func is non virtual on base");
        }

        public virtual void hello2()
        {
            Console.WriteLine("hello2 on deriv - virtual on deriv");
            Console.WriteLine("func is virtual on base");
        }

        public new virtual void hello3()
        {
            Console.WriteLine("hello3 on deriv - new and virtual on deriv");
            Console.WriteLine("func is non virtual on base");
        }

        public new virtual void hello4()
        {
            Console.WriteLine("hello4 on deriv - new and virtual on deriv");
            Console.WriteLine("func is virtual on base");
        }

        public override void hello5()
        {
            Console.WriteLine("hello5 on deriv - override on deriv");
            Console.WriteLine("func is virtual on base");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("base ref with base inst");
            baser b = new baser();
            b.hello();
            b.hello2();
            b.hello3();
            b.hello4();
            b.hello5();
            Console.WriteLine();

            Console.WriteLine("deriv ref with deriv inst");
            deriv d = new deriv();
            d.hello();
            d.hello2();
            d.hello3();
            d.hello4();
            d.hello5();
            Console.WriteLine();

            Console.WriteLine("base ref with deriv inst");
            baser o = new deriv();
            o.hello();
            o.hello2();
            o.hello3();
            o.hello4();
            o.hello5();
            Console.WriteLine();

            Console.WriteLine("deriv ref with deriv inst cast back to base ref");
            deriv s = new deriv();
            baser ds = s;
            ds.hello();
            ds.hello2();
            ds.hello3();
            ds.hello4();
            ds.hello5();

            Console.WriteLine("The end--");

            Console.ReadLine();
        }
    }
}

When we run the code, we can see this output:

base ref with base inst
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on base instance - virtual on base
func is override on deriv

deriv ref with deriv inst
hello on deriv - non virtual on deriv
func is non virtual on base
hello2 on deriv - virtual on deriv
func is virtual on base
hello3 on deriv - new and virtual on deriv
func is non virtual on base
hello4 on deriv - new and virtual on deriv
func is virtual on base
hello5 on deriv - override on deriv
func is virtual on base

base ref with deriv inst
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on deriv - override on deriv
func is virtual on base

deriv ref with deriv inst cast back to base ref
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on deriv - override on deriv
func is virtual on base
The end--

Points of Interest

I thought I was going crazy, I used the keyword virtual, I used new when the compiler warned me to use it, but I did not get the achieved result. That is until I used "override". And make sure you mark the base function as virtual if you do want to override it, otherwise compiler error!

Remember with polymorphism there are two things to consider, the type of the reference, and the type of the instance (what has been new'd).

Base b = new Base();        //Base reference type and base instance

Base b - new Derived();     //Base reference type and derived instance

Derived d = new Derived();  //Derived reference type and derived instance

Using the new keyword does not mean a derived instance will execute the derived function, it means the only way anything will call the derived function is if it is a derived reference type, it doesn't matter what the instance type is. Conversely, to execute an override function on the derived will happen if the instance is a derived type, irrespective of the type the reference pointing to the instance is !

So to recap:

  • Use override on a function that has a base signature that is virtual to override it
  • Use new to make one function hide another - so a non-virtual function has implementations in both the base and derived, you want them to have the same signature - use "new" to tell the compiler it is your intention (and get rid of the warning)
  • Use virtual to allow derived classes to override your function

So before you steam into C# thinking it was written to make transitioning from C++ seamless, think again, it was written to make the transition easy not seamless! There are a few gotchas, and this is just one. C# (which came from Java IMO) needs a little more attention than you think !

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here