Solving virtual member call in constructor
Asked Answered
G

5

7

I have an abstract class:

public abstract class ExampleBase : IExampleBase
{
    protected ExampleBase() 
    {
        this.SetupData();
    }

    protected abstract Dictionary<int, Adress> RelevantData { get; set; }

    protected abstract void SetupData();

    public void ProcessData() 
    {
        // use RelevantData
    }
}

And a derived class:

public class Example : ExampleBase
{
    public Example()
    {
    }

    protected override void SetupData()
    {
        this.RelevantData = new Dictionary<int, Adress>
        { 1, new Adress { ... } },
        { 2, new Adress { ... } }
    }
}

In the base class, ReSharper tells me

Virtual member call in constructor

I understand that it's dangerous to call the method because of the execution order.. but how can I resolve this issue?

Context: I want to set up data in each derived class which will then be processed in the base class. I wanted to call the SetupData() method in the base class since it's the same in every derived class.

Derived class:

  • Set up the data

Base class:

  • Process the data
Gnathion answered 20/4, 2015 at 13:18 Comment(9)
Well what does SetupData mean? Why does this have to be done in the ExampleBase constructor rather than in the derived class constructor? There isn't enough context here.Proviso
The SetupData() sets up some data (dependent on the derived class) to be further used in the base class. I wanted to call it in the base constructor so that i don't have to call it in every derived classGnathion
Is it used in the constructor for the base class? Why can't each derived class constructor just do whatever setup it needs?Proviso
Well I think this would solve the problem.. I just tried to do it in the base since it's the same in every derived class but I think you're right. So if I understand correctly: Call SetupData() in the constructor of every derived class and make these derived classes sealed?Gnathion
Well you don't necessarily have to seal the class... just make each class do its own setup, however it needs to. Some derived classes may not need any data set up. (I would kill the abstract member, for example.) But again, does the base class constructor need access to any of this? We still don't have enough context...Proviso
Oh okay. No, the base class constructor does not access any of these data.Gnathion
A common solution to this is making an "init" method that you're supposed to call after constructing the object, though I don't like that because of its brittleness. I would suggest carefully rethinking the general design (which we don't have enough information for).Royceroyd
I'm not sure what exact information I need to add.. I tried to describe the context in the edit of the original post... is this enough or what else do you need?Gnathion
I've added some more information. Are my intentions more understandable now? :)Gnathion
D
19

You don't. You accept the fact this is dangerous, and (try to) prevent this. This is a design flaw!

You could prevent this for example by moving the call to the highest level class, or make every class responsible for it's own, thus removing the unsafe part of the method call. Then you don't need another class (a base class) to take responsibility for its deriving classes.

If that isn't possible. Make very clear using comments or any other method available that the developer should take this problem into account when updating the code.

Demagnetize answered 20/4, 2015 at 13:23 Comment(0)
L
-1

Call SetupData in the constructor of Example (and every other derived class) not ExampleBase and make Example a sealed class.

The problem is that SetupData could access something that would be initialized by the Example constructor. But the Example constructor is called only after ExampleBase constructor has finished.

Litta answered 20/4, 2015 at 13:21 Comment(1)
I don't think this solves the issue. Since Example can be "Base" of another class, unless you seal it.Oleic
A
-1

Your base class constructor is called first. If your override method in your subclass depends on anything done in its constructor it won't work. Personally I'd look for a different design, maybe passing the abstract class into the derived class rather than using inheritance.

Appraisal answered 20/4, 2015 at 13:22 Comment(0)
F
-1

So a couple lines of code in every derived class
If you need to control the process order then you can do this

public abstract class MyBase 
{
    public void ProcessData()
    {
        bool processData = true;
    }
    public MyBase()
    {
        bool myBase = true;
    }
    public MyBase(int pass)
    {
        bool myBase = true;
    }
}
public class Example : MyBase
{
    public void GetData() {}
    public Example()
        : base(1)
    {
        bool example = true;
        GetData();
        ProcessData();
    }
} 
Featherbedding answered 20/4, 2015 at 13:54 Comment(1)
Such a convoluted answer, declaring multiple variables that do literally nothing.Halland
L
-1

Nowadays it's an option is to replace the abstract method with an Action (or Func ) parameter.

E.g.

public abstract class ExampleBase : IExampleBase
{
    protected ExampleBase(Action setupData) // new: Action parameter
    {
        setupData(); // new: call action instead of virtual method
    }

    protected abstract Dictionary<int, Adress> RelevantData { get; set; }

    public void ProcessData() 
    {
        // use RelevantData
    }
}

And a derived class:

public class Example : ExampleBase
{
    public Example(): base(SetupData) // new: Send SetupData methode
    {
    }

    private static void SetupData() // new: static and private
    {
        this.RelevantData = new Dictionary<int, Adress>
        { 1, new Adress { ... } },
        { 2, new Adress { ... } }
    }
}

Life answered 21/3 at 10:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.