Click here to Skip to main content
15,884,739 members
Articles / Programming Languages / C#
Tip/Trick

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

Rate me:
Please Sign up or sign in to vote.
4.71/5 (6 votes)
4 Jan 2017CPOL2 min read 15.4K   2   8
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

C++
Base *basePtr = new Derived();

C# Reference

C#
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:

C#
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).

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
Canada Canada
I have a blog here

http://www.electricalengineeringschools.org/blog/

And my robotics web site is here :

http://www.roboticsfordreamers.com/

I have been a software engineer for over 20 years now. I'm an expert in writing scalable restful web API services, which I've been doing for over 7 years now for games companies such as EA and Ubisoft. I've also worked on several video games, including Skate 2 and 3, NHL, Need for Speed, various Assassins Creed games, Far Cry 3 and 4, Driver San Francisco and most recently with a team of 15 on EA's UFC free to play. On this latest project I designed, implemented, tested (including load tests in Gatling and JUnit on AWS), deployed and supported all the web services for the game client, supporting up to a half a million active sessions.

I am proficient at programming in C, C++, C#, Java etc. I've developed many types of games, applications, SDKs and web services in Linux, Unix, iOS, Android and Windows.

My spare time is spent teaching and lecturing computing languages and science. I have a PhD in Artificial Intelligence (specialising in knowledge representation - Semantic networks with inference engines).

Currently I am also building an A.I. general purpose robot. The brains behind it are a mix of 6 Raspberry Pi and Banana Pros, with 2 USB cameras, SATA drive, Router Switch with inter-pi Comms, 2 motorised track systems, plus various sensors including motion/pir/sound etc.

The six pi's are split according to functions, including eyes (image processing/recognition), ears (speech processing and simulated speech),motor (object avoidance, environment mapping, drives all movement), entertainment (web browsing, media playing etc), brain (knowledge system, and adaptive learning/memory systems) and development system ( logging, diagnostics, debugging).

I am available as a consultant, so if you need to get something out the door quick, or want to set down an expandable and resilient framework, ping me !

Comments and Discussions

 
QuestionWelcome to the world of language design Pin
irneb10-Jan-17 22:02
irneb10-Jan-17 22:02 
AnswerRe: Welcome to the world of language design Pin
wkempf16-Jan-17 6:06
wkempf16-Jan-17 6:06 
QuestionThe issue is with C++, not C# Pin
John Brett5-Jan-17 22:53
John Brett5-Jan-17 22:53 
AnswerRe: The issue is with C++, not C# Pin
David O'Neil6-Jan-17 8:59
professionalDavid O'Neil6-Jan-17 8:59 
GeneralRe: The issue is with C++, not C# Pin
John Brett9-Jan-17 1:42
John Brett9-Jan-17 1:42 
QuestionA valid point, but rather trivial Pin
Daniel Pfeffer5-Jan-17 3:40
professionalDaniel Pfeffer5-Jan-17 3:40 
AnswerMessage Closed Pin
5-Jan-17 22:45
marcus obrien5-Jan-17 22:45 
GeneralRe: A valid point, but rather trivial Pin
wkempf16-Jan-17 6:02
wkempf16-Jan-17 6:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.