0

I have a multi-level class hierarchy with a Refresh method where each level introduces some new fields to be refreshed. In other words, I want the "level 3" class to refresh the fields of levels 1, 2 and 3, and the "level 4" class to refresh that of levels 1, 2, 3 and 4.

The basic solution is that each Refresh method calls base.Refresh. But I find this to be easy to overlook. I wish there was a way to annotate the root Refresh declaration to say that I want "constructor-like base call logic" on that method, i.e, please insert a call to base.Refresh before my method body for all children of that hierarchy. (If I can choose between pre and post-body i.e destructor-like, that's even better).

As far as I know there's no such thing in the base language, but I figure it's a common issue so perhaps some library introduces C# annotations that allow doing just that, as in the answer to Enforce super call on non constructor methods, or some suitable alternative. Is there ?

Julien BERNARD
  • 653
  • 6
  • 17
  • So `Level3 l = new Level4(); l.Refresh()` <-- should that refresh the level 4 fields or not? – canton7 Feb 13 '23 at 16:31
  • Yes for that use case ! – Julien BERNARD Feb 13 '23 at 16:32
  • Note that base constructors are not called implicitly either - you have to explicitly call `base(...)` in subclass constructors if you want to call base constructors. So I'm not sure what mechanism you're thinking of here. – D Stanley Feb 13 '23 at 16:33
  • I'm thinking of a hypothetical library that would let me write `@PrependBaseCalls` as an attribute on the level 1 method call. Although that would open the possibility of someone prepending the base call manually when the attribute already takes care of that... – Julien BERNARD Feb 13 '23 at 16:35
  • Mark every "refreshng" field with an attribute; Discover and refresh https://stackoverflow.com/a/156319/1704458 – T.S. Feb 13 '23 at 16:36
  • Template method pattern may help you. – Alexander Petrov Feb 13 '23 at 16:38
  • 1
    Presumably not what you want to hear. But a.) multi levels of inheritance isn't really something you should do. Composition is preferred since ages. b.) If you fear the call could be forgotten to propagate the refresh have unittest for it. – Ralf Feb 13 '23 at 16:42
  • Depends what you mean by "refresh" really. Can you generalise this mechanism, and e.g. have the base class keep a list of fields to refresh, which derived classes add to in their ctor? – canton7 Feb 13 '23 at 16:45

1 Answers1

0

You can implement this behavior with AOP and one of the libraries for Aspects.

For example you can add PostSharp to your project then define an aspect class that implements the "Refresh" method and applies it to the target classes using attributes.

public class RefreshCallAspect : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        if(args.Method.Name == "Refresh")
        {
            // here you should find and call a method of a base class
            base.OnInvoke(args);
        }
        args.Proceed();
    }
}

[RefreshCallAspect]
public class Level1
{
    public virtual void Refresh()
    {
        // Level 1 refresh logic here
    }
}

public class Level2 : Level1
{
    public override void Refresh()
    {
        // Level 2 refresh logic here
    }
}

public class Level3 : Level2
{
    public override void Refresh()
    {
        // Level 3 refresh logic here
    }
}

Here is an article about Intercepting Methods in PostSharp.

Vadim Martynov
  • 8,602
  • 5
  • 31
  • 43
  • Thanks ! I'll look into what you can do with PostSharp. I wonder if the attribute could be on the method rather than the class. – Julien BERNARD Feb 13 '23 at 16:51