Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I use virtual/override on class variables as I can on methods?

In the following example I am able to create a virtual method Show() in the inherited class and then override it in the inheriting class.

I want to do the same thing with the protected class variable prefix but I get the error:

The modifier 'virtual' is not valid for this item

But since I can't define this variable as virtual/override in my classes, I get the compiler warning:

TestOverride234355.SecondaryTransaction.prefix' hides inherited member 'TestOverride234355.Transaction.prefix'. Use the new keyword if hiding was intended.

Luckily when I add the new keyword everything works fine, which is ok since I get the same functionality, but this raises two questions:

  1. Why I can use virtual/override for methods but not for protected class variables?

  2. What is the difference actually between the virtual/override approach and the hide-it-with-new approach since at least in this example they offer the same functionality?

Code:

using System;

namespace TestOverride234355
{
    public class Program
    {
        static void Main(string[] args)
        {
            Transaction st1 = new Transaction { Name = "name1", State = "state1" };
            SecondaryTransaction st2 = 
                new SecondaryTransaction { Name = "name1", State = "state1" };

            Console.WriteLine(st1.Show());
            Console.WriteLine(st2.Show());

            Console.ReadLine();
        }
    }

    public class Transaction
    {
        public string Name { get; set; }
        public string State { get; set; }

        protected string prefix = "Primary";

        public virtual string Show()
        {
            return String.Format("{0}: {1}, {2}", prefix, Name, State);
        }
    }

    public class SecondaryTransaction : Transaction
    {
        protected new string prefix = "Secondary";

        public override string Show()
        {
            return String.Format("{0}: {1}, {2}", prefix, Name, State);
        }
    }
}
like image 526
Edward Tanguay Avatar asked Mar 04 '10 10:03

Edward Tanguay


1 Answers

Overriding a field does not really make sense. It's part of the state of the base class, and if an inheriting class wishes to change it, it should be changable in the inheriting class by giving it an appropriate visibility.

One thing you could do in your case is to set prefix in the constructor for the inheriting class:

// Base class field declaration and constructor
protected string prefix;

public Transaction()
{
  prefix = "Primary";
}

// Child class constructor
public SecondaryTransaction()
{
  prefix = "Secondary";
}

You can also make a property instead of a field, and make the property virtual. This will enable you to change the behavior of the getter and setter for the property in the inheriting class:

// Base class
public virtual string Prefix { get { /* ... */ } set { /* ... */ } }

// Child class
public override string Prefix { get { /* ... */ } set { /* ... */ } }

EDIT: As for your question of using a variable in a base constructor before an inheriting class has set it, one way to solve this is to define an initialization method in the base class, override it in the inheriting class, and call it from the base constructor before accessing any fields:

// Base class
public class Base
{
  protected string prefix;

  public Base()
  {
    Initialize();
    Console.WriteLine(prefix);
  }  

  protected virtual void Initialize()
  {
    prefix = "Primary";
  }
}

// Inheriting class
public class Child : Base
{
  public override void Initialize()
  {
    prefix = "Secondary";
  }
}

EDIT 2: You also asked what the difference between virtual/override and name hiding (the new keyword on methods) is, if it should be avoided, and if it can be useful.

Name hiding is a feature that breaks inheritance in the case of hiding virtual methods. I.e., if you hide the Initialize() method in the child class, the base class will not see it, and not call it. Also, if the Initialize() method was public, external code that was calling Initialize() on a reference of the base type would be calling Initialize() on the base type.

Name hiding is useful when a method is non-virtual in a base class, and a child wants to provide a different implementation of its own. Note, however, that this is NOT the same as virtual/override. References of the base type will call the base type implementation, and references of the child type will call the child type implementation.

like image 145
Håvard S Avatar answered Sep 21 '22 20:09

Håvard S