0

Thanks Eric Lippert's comprehensive comments in the comment section! I learn a lot from his comments. To share my learning with future readers, I've used parenthesis to wrap the wrong assumptions in my original question below so you can compare. And I have put the corrected assumptions in bold.

I read through this post Virtual member call in a constructor, and understand that we should not call overriddable methods in base class constructor, because the base class constructor (wrong: will invoke the virtual member in the base class when we want it to invoke the overridden member in the subclass) will invoke the overridden member in the subclass, but before subclass has fully initialized.

Now I have a base class with an abstract property, (wrong: not a virtual method), which is a still virtual member except that it must be overridden, and I'm using it in my constructor. Then I have my subclass overriding this property. My expectation is that when I create a new instance out of my subclass, the base constructor will be using the overridden property in the subclass. And this is what actually happened.

I'm wondering if this is a good practice, because I still get the same warning saying "Virtual member call in a constructor", (wrong: although technically I'm not using virtual members). I don't see any obvious anti-pattern here, the only potential one is that if I further subclass my existing subclass, things might get confusing, but I'm marking my first subclass sealed.

Below is an executable code sample, it will output: Name1, Name3

And I get the above warning in the base constructor line this.NeededNames.Contains(name)

So my code sample is simple enough to work because its virtual member NeededNames does not rely on anything in the TestNameList class. But as Eric explained in comments, if a subclass happens to have states that are depended by its abstract/virtual members, when the base constructor runs, the overridden member in the subclass will be called, but it will not be ready yet because the states have not been set up by the subclass constructor. In this case, the subclass instance would not be initialized properly.

This sample is not a good practice although it works, because in reality we don't know what subclass could others create from our base class, and if a new subclass has states that need to be set up during the subclass construction, then we would not be able to instantiate it correctly from the base class constructor. Outside of this question, to make this sample work in a better way, I created an Initialize method in the base class that will be invoked after every subclass is instantiated.

public class Program
{
    public static void Main()
    {
        NameListBase list = new TestNameList();
        list.Test();
    }
}

public abstract class NameListBase
{
    protected IList<string> ActualNames { get; } = new List<string>();

    IList<string> Names { get; } = new List<string>
    {
        "Name1", "Name2", "Name3", "Name4"
    };

    protected abstract IList<string> NeededNames { get; }

    protected NameListBase()
    {
        foreach (string name in this.Names)
        {
            if (this.NeededNames.Contains(name))
            {
                this.ActualNames.Add(name);
            }
        }
    }

    public abstract void Test();
}

public sealed class TestNameList : NameListBase
{
    protected override IList<string> NeededNames { get; } = new List<string>
    {
        "Name1", "Name3"
    };

    public override void Test()
    {
        Console.WriteLine(string.Join(", ", this.ActualNames));
    }
}
Kun Hu
  • 417
  • 5
  • 11
  • You should initialize properties in constructors, instead of overriding since overriding is for code of the getters and setters. –  Nov 13 '19 at 18:10
  • "because the base class constructor will *invoke the virtual member in the base class* when we want it to invoke the overridden member in the subclass"??? Where did you get that from? – Alexei Levenkov Nov 13 '19 at 18:12
  • I'm glad you asked this question because it shows that you have a considerable number of false beliefs. Let's correct some of those so that you can be more successful in the future. – Eric Lippert Nov 13 '19 at 18:14
  • First, your understanding of the linked post is *wrong and backwards*. You say "the base class constructor will invoke the virtual member in the base class when we want it to invoke the overridden member in the subclass." No, that is the *opposite* of what happens. The overriding implementation is invoked. But that's not the important part of your misunderstanding. – Eric Lippert Nov 13 '19 at 18:15
  • The question you've linked to explains why it is bad idea (call method on partially constructed object)... but somehow you did not get that... At this point this question is duplicate of one you've linked to... but if you [edit] to clarify what you need to be explained differently this could be re-opened. – Alexei Levenkov Nov 13 '19 at 18:16
  • 3
    The important part is your belief about what the bad thing is. The problem is not that the wrong method is being invoked. The right method is being invoked. **The problem is that a method of the derived class is being invoked before the constructor of the derived class has initialized the derived state, which can cause the method to crash or misbehave**. – Eric Lippert Nov 13 '19 at 18:16
  • 3
    You also have a wrong understanding of what "abstract" means. You say "I have a base class with an abstract property, not a virtual method" but an abstract property is logically the same thing as a pair of abstract methods -- the getter and the setter, and **abstract methods are virtual methods**. You say "I'm not using virtual members" but **abstract members are virtual members**. Virtual and abstract are not opposites, they are synonyms! An abstract is just a virtual that must be overridden. – Eric Lippert Nov 13 '19 at 18:17
  • The reason you are getting the warning about this being a bad practice is because **this is a bad practice**. Suppose your base class calls the virtual property getter. The getter might be returning a value based on a field that has not been initialized yet! Suppose your base class calls the virtual property setter. The setter might change state *that is then overwritten by the derived class constructor*. Either way it is *super dangerous* to use a virtual property before the derived class constructor runs. – Eric Lippert Nov 13 '19 at 18:19
  • Now, on the positive side, sealing your subclass is a great idea and you should keep on doing that. – Eric Lippert Nov 13 '19 at 18:23
  • @AlexeiLevenkov: It's possible that the original poster is getting this idea from C++. In C++ when you call a virtual method from a base class, the method that is invoked is the override in the base class, not the derived class. In implementations of C++ that use vtables, the vtable *changes* as the constructors run! In C#, the vtable is initialized by the allocator and stays the same through the lifetime of the object. – Eric Lippert Nov 13 '19 at 18:25
  • 1
    @EricLippert sure... that was my first idea too (before I checked what OP linked to) - but OP linked C# explanation that explicitly says otherwise (as you've noted)… Hopefully your extremely nice and detailed comments clarified OP's misunderstandings. – Alexei Levenkov Nov 13 '19 at 18:32
  • @EricLippert Thanks a lot Eric! I've just updated my question to reflect my understanding of your comments. – Kun Hu Nov 14 '19 at 00:54
  • @AlexeiLevenkov Thanks Alexei! Eric had addressed all my confusions. I think my question should be kept as is and not be opened. – Kun Hu Nov 14 '19 at 00:56

0 Answers0