Search code examples
c#oopinterfaceabstract-classradix

Unsure when to use 'base' in C#


I'm trying to teach myself about OOP in C#, but I have a question about when to use base. I understand the general principles, but I'm not sure what's best in the example below. This simple test includes:

  • An interface with two string properties
  • An abstract class that implements this interface and adds a couple more string properties
  • Two classes that implement the abstract class. One uses base and the other doesn't, but they both produce the same output when the program is executed.

My question is: in this example, is one implementation more desirable than the other? I'm not really sure if there are any meaningful differences between TranslationStyleA and TranslationStyleB, or if it's just down to personal preference?

Many thanks for your time and thoughts!

using System;

namespace Test
{
    interface ITranslation
    {
        string English { get; set; }
        string French { get; set; }
    }

    public abstract class Translation : ITranslation
    {
        public virtual string English { get; set; }
        public virtual string French { get; set; }

        public string EnglishToFrench { get { return English + " is " + French + " in French"; } }
        public string FrenchToEnglish { get { return French + " is " + English + " in English"; } }

        public Translation(string e, string f)
        {
            English = e;
            French = f;
        }
    }

    public class TranslationStyleA : Translation
    {
        public override string English
        {
            get { return base.English; }
            set { base.English = value; }
        }

        public override string French
        {
          get { return base.French; }
          set { base.French = value; }
        }

        public TranslationStyleA(string e, string f) : base(e, f)
        {
        }
    }

    public class TranslationStyleB : Translation
    {
        private string english;
        public override string English
        {
            get { return english; }
            set { english = value; }
        }

        private string french;
        public override string French
        {
            get { return french; }
            set { french = value; }
        }

        public TranslationStyleB(string e, string f) : base(e, f)
        {
            this.English = e;
            this.French = f;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            TranslationStyleA a = new TranslationStyleA("cheese", "fromage");
            Console.WriteLine("Test A:");
            Console.WriteLine(a.EnglishToFrench);
            Console.WriteLine(a.FrenchToEnglish);

            TranslationStyleB b = new TranslationStyleB("cheese", "fromage");
            Console.WriteLine("Test B:");
            Console.WriteLine(b.EnglishToFrench);
            Console.WriteLine(b.FrenchToEnglish);

            Console.ReadKey();
        }
    }
}

Solution

  • The first thing that you need to understand is what's going on when you have an automatic property:

    public virtual string English { get; set; }
    

    Behind the scenes, the compiler is generating a private field, and getting/setting that private field when you access the property. It is equivalent to this

    private string _english;
    public virtual string English { get { return _english; } set { _english = value; } }
    

    except that you don't know the name of the private field, and so you cannot access it.

    So in your TranslationStyleA class, you are not actually doing anything with the English property, because it just accesses the base class's property directly and doesn't change it's behavior.

        // None of this is even needed- we are just delegating to the base class
        public override string English
        {
            get { return base.English; }
            set { base.English = value; }
        }
    

    Now in the TranslationStyleB class, you are actually changing the behavior of the property (albeit in a fairly useless way). Instead of storing the value for the English property in the base class's auto-implemented private variable, you are storing it in the private variable defined at the derived class level:

        private string english;
        public override string English
        {
            get { return english; }
            set { english = value; }
        }
    

    Neither of these implementations does anything of course, and as implemented neither is needed, since the base class implements the properties perfectly fine itself. So my answer to your original question is that neither is preferred, given the code as you describe it.

    Now, let's look at an example where your question is relevant. You only need to override them if you want to change their behavior, for instance.

        // We don't want any leading or trailing whitespace, so we remove it here.
        public override string English
        {
            get { return base.English; }
            set { base.English = value.Trim(); }
        }
    

    We want to delegate to the base class here, because of why these were properties in the first place. Semantically, a property is the same as a field:

    public String Foo;
    public String Foo { get; set; } // <-- why bother with all this extra { get; set; } stuff?
    

    The reason is that from the compiler's perspective, it is a breaking change in an interface to go from a property to a field. So if I change

    public String Foo;
    

    to

    public String Foo { get; set; }
    

    Then any code that depends on my code needs to be recompiled. However, if I change

    public String Foo { get; set; }
    

    to

    private string _foo;
    public String Foo { get { return _foo; } set { _foo = value.Trim(); } }
    

    then dependent code still only sees the public property, and does not need recompilation (because the interface of my class has not changed).

    If the base class here (Translation) were to change it's behavior for the property English thus:

    private string _english;
    public String English { get { return _english; } set { _english = value.ToUpper(); } }
    

    the you would want to pick that up in your derived classes!

    So considering that properties have behavior associated with them, you should always delegate to the parent class implementation unless that implementation has undesirable effects in your deriving class.