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

Safely Calling Virtual Members From Constructors...

Rate me:
Please Sign up or sign in to vote.
2.86/5 (5 votes)
23 Mar 2015CPOL1 min read 17.9K   5   18
Many programmers know what calling virtual members from not sealed class constructor may cause runtime error, because some members used in virtuals could be not initialized. I will show a simple way to call virtuals safely from constructors of inherited classes.

Introduction

The idea is simple - you should make calls to virtual members only from the top of the inheritance hierarchy. This tip shows it can be done in a simple manner.

Background

You can read more about the solving problem here or try to find on the internet - 'virtual member call from constructor'.

Using the Code

Let's suppose we have three simple classes A, B, C inherited in such sequence A -> B -> C.

A has virtual member, B and C override it and use it in their constructors.

Code A: Will cause runtime error, because there are not initialized members in B and C.

C#
class A
{
    public A()
    {
        VirtualMethod();
    }

    protected virtual void VirtualMethod()
    {
        Console.WriteLine("A: Do nothing");
    }
}

class B : A
{
    private readonly String _someString;

    public B(String someString)
        :base () //Just to show that base constructor called, may be skipped
    {
        if(someString == null) throw new ArgumentNullException("someString");
        _someString = someString;

        VirtualMethod();
    }

    protected override void VirtualMethod()
    {
        Console.WriteLine("B : Length of some string - {0}", _someString.Length);
    }
}

class C : B
{
    private readonly String _someString;

    public C(String someStringC, String someStringB)
        : base(someStringB) //Just to show that base constructor called, may be skipped
    {
        if (someStringC == null) throw new ArgumentNullException("someStringC");
        _someString = someStringC;

        VirtualMethod();
    }

    protected override void VirtualMethod()
    {
        Console.WriteLine("C : Length of some string - {0}", _someString.Length);
    }
}

To solve a problem, we should make some changes to base and inherited classes, these changes will be visible in the following code:

Code B: Will not cause any errors.

C#
class A
{
    private List<Action> _virtualMembersCallActions;

    /// <summary>
    /// This function should be called from every inherited  constructor,
    /// if you forget to call it you will have some not initialized
    /// members through all inheritance hierarchy be careful!
    /// </summary>
    /// <typeparam name="TType">Type of currently calling class.</typeparam>
    /// <param name="action">Action which contains virtual members calls.</param>
    protected void CallVirtualMembers<TType>(Action action = null)
    {
        if (action != null)
        {
            if (_virtualMembersCallActions == null)
            {
                _virtualMembersCallActions = new List<Action>();
            }

            if (!_virtualMembersCallActions.Contains(action))
            {
                _virtualMembersCallActions.Add(action);
            }
        }

        if (GetType() == typeof (TType))
        {
            _virtualMembersCallActions.ForEach(i=>i());
            _virtualMembersCallActions = null;
        }
    }

    public A()
    {
        CallVirtualMembers<A>(VirtualMethod);
    }

    protected virtual void VirtualMethod()
    {
        Console.WriteLine("A: Do nothing");
    }
}

class B : A
{
    private readonly String _someString;

    public B(String someString)
        :base () //Just to show that base constructor called, may be skipped
    {
        if(someString == null) throw new ArgumentNullException("someString");
        _someString = someString;

        CallVirtualMembers<B>(VirtualMethod);
    }

    protected override void VirtualMethod()
    {
        Console.WriteLine("B : Length of some string - {0}", _someString.Length);
    }
}

class C : B
{
    private readonly String _someString;

    public C(String someStringC, String someStringB)
        : base(someStringB)
    {
        if (someStringC == null) throw new ArgumentNullException("someStringC");
        _someString = someStringC;

        CallVirtualMembers<C>(VirtualMethod);
    }

    protected override void VirtualMethod()
    {
        Console.WriteLine("C : Length of some string - {0}", _someString.Length);
    }
}

Of course, this just a sample, you can add any business logic to CallVirtualMembers function, you can have several such functions to change sequence if virtual members are calling. In my current example, I have added:

C#
if (!_virtualMembersCallActions.Contains(action))
{
      _virtualMembersCallActions.Add(action);
}

to suppress repeat calling of same virtual members twice or more times, if you remove this line you will see repeats in console window.

Code C: No errors, but use singleton extension

C#
public static class VirtualsCaller
{
    private static readonly Dictionary<Object, List<Action>> _actionsStore = new Dictionary<object, List<Action>>();

    /// <summary>
    /// This function should be called from every inherited  constructor,
    /// if you forget to call it you will have some not initialized
    /// members through all inheritance hierarchy be careful!
    /// </summary>
    /// <typeparam name="TType">Type of currently calling class.</typeparam>
    /// <param name="source">Source object</param>
    /// <param name="action">Action which contains wirtual members calls.</param>
    public static void CallVirtualMembers<TType>(Object source, Action action)
    {
        List<Action> actions;
        if (!_actionsStore.TryGetValue(source, out actions))
        {
                    actions = new List<Action>();
                    _actionsStore[source] = actions;
        }

        if (action != null)
        {
            if (!actions.Contains(action))
            {
                actions.Add(action);
            }
        }

        if (source.GetType() == typeof(TType))
        {
            actions.ForEach(i => i());
            _actionsStore.Remove(source);
        }
    }
}

class A
{
    public A()
    {
        VirtualsCaller.CallVirtualMembers<A>(this, VirtualMethod);
    }

    protected virtual void VirtualMethod()
    {
        Console.WriteLine("A: Do nothing");
    }
}

class B : A
{
    private readonly String _someString;

    public B(String someString)
        :base () //Just to show that base constructor called, may be skipped
    {
        if(someString == null) throw new ArgumentNullException("someString");
        _someString = someString;

        VirtualsCaller.CallVirtualMembers<B>(this, VirtualMethod);
    }

    protected override void VirtualMethod()
    {
        Console.WriteLine("B : Length of some string - {0}", _someString.Length);
    }
}

class C : B
{
    private readonly String _someString;

    public C(String someStringC, String someStringB)
        : base(someStringB) //Just to show that base constructor called, may be skipped
    {
        if (someStringC == null) throw new ArgumentNullException("someStringC");
        _someString = someStringC;

        VirtualsCaller.CallVirtualMembers<C>(this, VirtualMethod);
    }

    protected override void VirtualMethod()
    {
        Console.WriteLine("C : Length of some string - {0}", _someString.Length);
    }
}

History

  • 23/03/2015 - Just created

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Saber Interactive
Russian Federation Russian Federation
My specializations:

C# (especially multithreading)
WPF (MVVM, styling)
WCF (message inspectors, configuration)
MSSQL (administartion, creation, procedures, recursive queries, bulk processing)

Comments and Discussions

 
QuestionSeparate construction/new from initialization Pin
englebart24-Mar-15 11:08
professionalenglebart24-Mar-15 11:08 
AnswerRe: Separate construction/new from initialization Pin
Evgeny Bestfator24-Mar-15 21:08
professionalEvgeny Bestfator24-Mar-15 21:08 
AnswerRe: Separate construction/new from initialization Pin
Evgeny Bestfator24-Mar-15 21:10
professionalEvgeny Bestfator24-Mar-15 21:10 
GeneralRe: Separate construction/new from initialization Pin
Evgeny Bestfator24-Mar-15 21:10
professionalEvgeny Bestfator24-Mar-15 21:10 
GeneralRe: Separate construction/new from initialization Pin
ArchAngel12325-Mar-15 10:35
ArchAngel12325-Mar-15 10:35 
GeneralRe: Separate construction/new from initialization Pin
Evgeny Bestfator25-Mar-15 22:46
professionalEvgeny Bestfator25-Mar-15 22:46 
GeneralRe: Separate construction/new from initialization Pin
englebart6-Apr-15 2:11
professionalenglebart6-Apr-15 2:11 
GeneralRe: Separate construction/new from initialization Pin
Evgeny Bestfator6-Apr-15 12:08
professionalEvgeny Bestfator6-Apr-15 12:08 
SuggestionSimpler solution Pin
ArchAngel12324-Mar-15 10:28
ArchAngel12324-Mar-15 10:28 
I'm an Occum's Razor person - this seems like a way too complicated solution in search of a problem.

If you really have a need to call a method in your constructors that you're also allowing to be overridden by decedent classes - and that's REALLY the design you think you need - then clearly you cannot possibly be wanting any decedent's method executed at the time of the base constructors since you can't possibly know what that method will really be doing - if in fact you DO want some unknown future decedent's unknown functionality being called at a base class constructor level (with all the indeterminate behavior and unexpected consequences that is likely to produce), then you probably need to find a new designer

If on the other hand you're sane and you just happen to be using a virtual method in the constructor BUT expect at the time of that constructor executing that you would be using the state of the virtual method as defined by that base class, then the solution is pretty simple:

Class A
public A() { _VirtualMethod(); }
protected virtual void VirtualMethod() { _VirtualMethod(); }
private void _VirtualMethod() { Console.WriteLine("A"); }

Class B
public B(string x) : base() { mystring = x; _VirtualMethod(); }
protected override void VirtualMethod() { _VirtualMethod(); }
private void _VirtualMethod() { Console.WriteLine("B - " + mystring); }

Class C
public C(string x, string x2) : base(x) { mystring = x; mystring2 = x2; _VirtualMethod(); }
protected virtual void VirtualMethod() { _VirtualMethod(); }
private void _VirtualMethod() { Console.WriteLine("C - " + mystring + " - " + mystring2); }

This executes in a deterministic manner the way C++ constructors and virtual methods would

modified 24-Mar-15 16:37pm.

GeneralRe: Simpler solution Pin
Evgeny Bestfator24-Mar-15 21:38
professionalEvgeny Bestfator24-Mar-15 21:38 
GeneralRe: Simpler solution Pin
ArchAngel12325-Mar-15 10:29
ArchAngel12325-Mar-15 10:29 
GeneralRe: Simpler solution Pin
Evgeny Bestfator25-Mar-15 23:05
professionalEvgeny Bestfator25-Mar-15 23:05 
GeneralComplex solution to a problem that shouldn't exist Pin
John Brett24-Mar-15 0:22
John Brett24-Mar-15 0:22 
GeneralRe: Complex solution to a problem that shouldn't exist Pin
Evgeny Bestfator24-Mar-15 0:51
professionalEvgeny Bestfator24-Mar-15 0:51 
GeneralRe: Complex solution to a problem that shouldn't exist Pin
John Brett24-Mar-15 1:50
John Brett24-Mar-15 1:50 
GeneralRe: Complex solution to a problem that shouldn't exist Pin
Evgeny Bestfator24-Mar-15 2:24
professionalEvgeny Bestfator24-Mar-15 2:24 
QuestionNice try... Pin
FatCatProgrammer23-Mar-15 5:10
FatCatProgrammer23-Mar-15 5:10 
AnswerRe: Nice try... Pin
Evgeny Bestfator23-Mar-15 21:10
professionalEvgeny Bestfator23-Mar-15 21:10 

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.