2

I wish to have a debug logger in a class that should be extended. Is there a way to do this without having to add the log entry on every derived class?

using System;

public abstract class Phase {
    public virtual void Start() {
        Console.Write("[LOG] Start");
    }

    /// <summary>
    /// Returns true when finished.
    /// </summary>
    public virtual bool Update() {
        Console.Write("[LOG] Update");
        return true;
    }

    public virtual void End() {
        Console.Write("[LOG] End");
    }
}
public class PhaseMenu : Phase {
    private byte _counter;

    public override void Start() {
        _counter = 0;
    }
    
    public override bool Update() {
        _counter++;
        if ( _counter > 100 ) {
            return true;
        }
        return false;
    }

    public override void End() {

    }
}

Desired outcome is to have the LOGs be written whenever PhaseMenu's methods are called. So that the methods aren't overriding but rather extending.

Dorjan
  • 2,027
  • 3
  • 16
  • 29
  • 1
    Did you not forget to call in particular and for example `base.Start()` from `PhaseMenu` ? –  Dec 06 '20 at 11:55
  • I was wondering if there was a way to do this without having to write base.me() on every method? – Dorjan Dec 06 '20 at 11:56
  • No, as I know. You use virtuality so this is [polymorphism](https://stackoverflow.com/questions/1031273/what-is-polymorphism-what-is-it-for-and-how-is-it-used/58197730#58197730): you need to call base to propagate behavior or to not call to break it. I hope this can help you to enjoy C# coding: [How do I improve my knowledge in C#](http://www.ordisoftware.com/files/stack-overflow/CsharpBegin.htm) –  Dec 06 '20 at 11:57
  • Are you saying there's no way to do this in C# at all? Or just if I use virtual? I understand it isn't possible with virtual hence my question is asking if there's a better method. My appologies if I was unclear? – Dorjan Dec 06 '20 at 12:00
  • Yes. You must write the call to the base method. There is no compiler flag, no attribute or something else, as I know, that enforce always calling base, because it is impossible to do that. You have the choice to call or to not call the base, where you want and as you want. It is impossible for the compiler to have an automatic polymorphic base calling in C# and similar languages, except for the default constructor, because of the OOP theory and mechanisms used and offered by these languages. –  Dec 06 '20 at 12:02
  • 1
    OK. Great to know. Thank you. If you post an answer saying "No. But you can use Base.MethodName()" I will mark as correct. – Dorjan Dec 06 '20 at 12:04
  • Does @LiquidCore answer better match and solve your goal ? If not I'll post my comment as answer. –  Dec 06 '20 at 12:11

3 Answers3

1

Reading your question and the relative comments, the only way out I can think of is implementing some kind of event based structure. This is way more than a hassle of just writing base.Log(), but it may do the thing for you.

Basically you can subscribe every method to an event or something like that and said event logs. That would do it automagically.

To add to that, the event subscription can be done programmatically, (through reflection or other means), so it would be a few lines of code.

EDIT: Here is a sample to work it out with reflection

Liquid Core
  • 1
  • 6
  • 27
  • 52
  • Indeed, if the use of events corresponds to the objective of the OP, it can perhaps offer the desired pool of chained handler methods: https://stackoverflow.com/questions/1645478/order-of-event- handler-execution –  Dec 06 '20 at 12:09
  • I wish to reply that I've seen this but need to leave my desk for a few hours. I have thought about events before but I'll read the articles and see if it inspires an appropriate solution. Thank you. – Dorjan Dec 06 '20 at 12:21
  • Oh I see, so using reflection I set up a log class that will take a Type and record the detail based upon the event fired. You're right that this may be over-engineering at its finest but might be something to think about. – Dorjan Dec 06 '20 at 15:16
  • @Dorjan Yes, I added a link to my answer which might be helpful to understand how to work it out through reflection. Please mark as answer if it has been helpful. – Liquid Core Dec 10 '20 at 08:04
1

@Dorjan maybe an idea as well:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        var phaseMenu = new PhaseMenu();
        
        phaseMenu.Start();
        phaseMenu.Update();
        phaseMenu.Update();
        phaseMenu.Update();
        phaseMenu.End();
    }
}

public abstract class Phase 
{
    private readonly Action _onStart;
    private readonly Func<bool> _onUpdated;
    private readonly Action _onEnd;

    protected Phase(Action aOnStart, Func<bool> aOnUpdated, Action aOnEnd)
    {
        _onStart = aOnStart;
        _onUpdated = aOnUpdated;
        _onEnd = aOnEnd;
    }
        
    public void Start() 
    {
        Console.WriteLine("[LOG] Start");
        _onStart?.Invoke();
    }
        
    public bool Update() 
    {
        Console.WriteLine("[LOG] Update");
        return _onUpdated?.Invoke() ?? false;
    }

    public void End() 
    {
        Console.WriteLine("[LOG] End");
        _onEnd?.Invoke();
    }
}
    
public class PhaseMenu : Phase 
{
    private static byte _counter;

    public PhaseMenu() : base(OnStart, OnUpdated, OnEnd)
    { }

    private static void OnStart()
    {
        _counter = 0;
    }
    
    private static bool OnUpdated()
    {
        _counter++;
        return _counter > 100;
    }
    
    private static void OnEnd()
    {
        Console.WriteLine("That is all folks!");
    }
}

Output:

Hello World!
[LOG] Start
[LOG] Update
[LOG] Update
[LOG] Update
[LOG] End
That is all folks!

The idea is that your base class implements a certain contract for its implementors to "follow". An example that I gave is limited to certain ways of providing implementation, but you can always provide a method to "register" the behavior of the methods.

I think that this can be improved further, depending on the needs and what needs to be achieved, but hope this gives an idea of what I was thinking as well.

Edit:

I just saw that idea about events was posted, so this is somewhat of a simplified implementation of it.

D34NM
  • 101
  • 7
-1

Don't define your Update method as virtual in the base class, then have a new protected virtual (or abstract) method called PhaseUpdate() which you call from within Update. Thereby forcing all calls to update through the base method.

using System;

public abstract class Phase {
    public void Start() {
        Console.Write("[LOG] Start");
        PhaseStart();

    }

    /// <summary>
    /// Returns true when finished.
    /// </summary>
    public bool Update() {
        Console.Write("[LOG] Update");
        return PhaseUpdate();
    }

    public void End() {
        Console.Write("[LOG] End");
        PhaseEnd();
    }

protected void PhaseStart() {
        
    }

    /// <summary>
    /// Returns true when finished.
    /// </summary>
    protected virtual bool PhaseUpdate() {
       
        return true;
    }

    protected virtual void PhaseEnd() {
        
    }

}
public class PhaseMenu : Phase {
    private byte _counter;

    protected override void PhaseStart() {
        _counter = 0;
    }
    
    protected override bool PhaseUpdate() {
        _counter++;
        if ( _counter > 100 ) {
            return true;
        }
        return false;
    }

    protected override void PhaseEnd() {

    }
}

Jim
  • 479
  • 2
  • 8