6

I want to add a common callback function to all the methods of an object. I found this SO question but in that aproach, I would have to modify every function of the class. I would like to define some beforeFilter method that get trigered before every method call. I know I could use Action delegates for that but I don't know how.

UPDATE: As an example, what I want would be something like this

Class MyClass

    Sub MyCallback
        'callback code...
    End Sub

    'Rest of tyhe code

End Class

As you can see, the ideal would be to add a simple (or more than one) method to get trigered before all the rest of the methods. Maybe with some base class or an interface, I don't know (that's why I ask for).

Some other option that I think it might be posible is to clone the object and define a new class instead to add code to the existing one. So in that Class use the Object to get access to the methods trhough the Type

Class NewClass

     Sub AddCallback(Obj As Object, Callback As Action)
          'Add the callback here
     End Sub
End Class

I'm just guessing, maybe some of my option are even unviable, so please help my on this

Closest approach

What I have for now, is this method

Sub RunWithCallback(beforFilter As Action, f As Action)

    beforeFilter()
    f()

End Sub

(This means a lot of overloads for the RunWithCallback to manage Sub's and Functions with Actions and Funcs delegates, with different number of parameters) To use this, I would have to run this Sub instead of calling any of the Object methods (passing ByRef parameter to catch returning value in functions). Something like this

Public Sub DoNothing()
    Debug.WriteLine("Callback working!")
End Sub

Public Sub BeforeFilter()
    Debug.WriteLine("Testing callback...")
End Sub

'To run the above function    
RunWithCallback(AddressOf BeforeFilter, AddressOf DoNothing)

I think there should be a better solution to this, something that allow to avoid calling the same sub, and avoid the overloads, some kind of dynamic extension method for the object Class

Community
  • 1
  • 1
Yerko Palma
  • 12,041
  • 5
  • 33
  • 57
  • Some example code might be useful. As is, it is very unclear what you want. For instance, is `beforeFilter` supposed to be called before the method is invoked or at the start of each method? An `Action` delegate will still require refactoring, so that doesnt seem like it is what you want. – Ňɏssa Pøngjǣrdenlarp Apr 16 '15 at 15:33
  • Check my edit, if you still have doubts just comment again. – Yerko Palma Apr 16 '15 at 16:14
  • If `MyCallback` is in `MyClass` and invoked from `MyClass` why cant the class just invoke it (that is nothing like a 'callback'). Its more common to use a delegate (not necessarily `Action`) when a method *doesnt* have access to a method you wish to use *from somewhere else*. For instance, `ClassA.DoFoo` is invoked from ClassB and in the course of `DoFoo` you need it to invoke `ClassB.GetBar`. – Ňɏssa Pøngjǣrdenlarp Apr 16 '15 at 19:05
  • I have access to the class that I want to check, but that that class has ~1k lines of code, and I have some other classes like that, so add an invoke of a method before every method of the class is not a good option – Yerko Palma Apr 16 '15 at 19:13
  • 2
    Sorry your question is still muddled. Your example of a beforeFilter method to run before any/every other local method invoked doesnt really describe a callback at all. You should clarify *what* you want to do, not *how* or the only answers you will get will be based on the title and be delegate related. – Ňɏssa Pøngjǣrdenlarp Apr 17 '15 at 15:38
  • Don't know if you have seen, i have updated my answer according to your edits. Maybe the `MyCallbacks(params MyDelegate[] callbacks)` method is what you looking for. – k1ll3r8e Apr 21 '15 at 11:26

2 Answers2

1

Here is an example how delegates work and how u can use them:

public class MyClass
{
    // MyDelegate callback holder
    private MyDelegate _myDelegate;

    // Singleton holder
    private static volatile MyClass _instance;

    ///<summary>
    /// Instance property
    ///</summary>
    public static MyClass Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new MyClass();
            }
            _instance.MyCallbacks(_instance.MyDelegateCallback, _instance.MyDelegateCallback1);
            return _instance;
        }
    }

    ///<summary>
    /// Instance multi-callback caller
    ///</summary>
    ///<param name="callbacks">calbacks to call</param>
    ///<returns>MyClass</returns>
    public static MyClass Instance(params MyDelegate[] callbacks)
    {
        if (_instance == null)
        {
            _instance = new MyClass();
        }
        _instance.MyCallbacks(callbacks);
        return _instance;
    }

    ///<summary>
    /// MyClass constructor
    ///</summary>
    private MyClass()
    {
        // Define the callback
        MyDelegate = MyDelegateCallback;
    }

    ///<summary>
    /// MyDelegate property, here u can change the callback to any function which matches the structure of MyDelegate 
    ///</summary>
    public MyDelegate MyDelegate
    {
        get
        {
            return _myDelegate;
        }
        set
        {
            _myDelegate = value;
        }
    }

    ///<summary>
    /// Call all given callbacks
    ///</summary>
    ///<param name="callbacks">callbacks u want to call</param>
    public void MyCallbacks(params MyDelegate[] callbacks)
    {
        if (callbacks != null)
        {
            foreach (MyDelegate callback in callbacks)
            {
                if (callback != null)
                {
                    callback.Invoke(null);
                }
            }
        }
    }

    ///<summary>
    /// RunTest, if u call this method the defined callback will be raised
    ///</summary>
    public void RunTest()
    {
        if (_myDelegate != null)
        {
            _myDelegate.Invoke("test");
        }
    }

    ///<summary>
    /// MyDelegateCallback, the callback we want to raise
    ///</summary>
    ///<param name="obj"></param>
    private void MyDelegateCallback(object obj)
    {
        System.Windows.MessageBox.Show("Delegate callback called!");
    }

    ///<summary>
    /// MyDelegateCallback1, the callback we want to raise
    ///</summary>
    ///<param name="obj"></param>
    private void MyDelegateCallback1(object obj)
    {
        System.Windows.MessageBox.Show("Delegate callback (1) called!");
    }
}

///<summary>
/// MyDelegate, the delegate function
///</summary>
///<param name="obj"></param>
public delegate void MyDelegate(object obj);

I updated my answer, have a look at MyCallbacks(params MyDelegate[] callbacks). It will call different callbacks which match the structure of MyDelegate.
Edit:
Added MyClass.Instance & MyClass.Instance(params MyDelegate[] callbacks).

k1ll3r8e
  • 729
  • 16
  • 22
  • In your answer I can see you can handle different structures of callbacks, but how would that callbacks be called before the rest of the class methods? – Yerko Palma Apr 21 '15 at 12:40
  • I edited my answer, i added `MyClass.Instance`. In the "get" method i added `MyCallbacks(params MyDelegate[] callbacks)` with 2 test callbacks. All the time you have to call a method, call it like `MyClass.Instance.MyMethodToCall()` because all the time you call `MyClass.Instance` your defined callbacks will be called. - I think there is no better solution. - Except, you add the `MyCallbacks(params MyDelegate[] callbacks)` with the needed callbacks to each method you currently have. – k1ll3r8e Apr 21 '15 at 21:59
  • 2nd edit, i added additional `MyClass.Instance(params MyDelegate[] callbacks)` so you can also call special callbacks if needed. – k1ll3r8e Apr 21 '15 at 22:15
  • This is similar to what I have so far, but with more flexibility. The goal was to have something like what @VMAtm posted, but I couldn't make it work... – Yerko Palma Apr 22 '15 at 16:02
  • 2
    I wish, i could help with this, but, i never used an "Aspect library". - I found something interesting [here](http://stackoverflow.com/a/25820) this might help you. – k1ll3r8e Apr 22 '15 at 19:02
  • 2
    Well your approach is the closest to what I need, so I will use this. Thanks for your answer – Yerko Palma Apr 22 '15 at 21:01
1

Have you ever considered to use an Aspect library? Up to your question it seems like you really can solve your problem with this approach.

For example, you can use the PostSharp library here, simply derive the OnMethodBoundaryAspect class, and handle the four available main events:

or two additional:

  • OnResume
  • OnYield

or any of them. The simple schema is this:

int MyMethod(object arg0, int arg1)
{
    OnEntry();
    try
    {
        // YOUR CODE HERE 
        OnSuccess();
        return returnValue;
    }
    catch ( Exception e )
    {
        OnException();
    }
    finally
    {
        OnExit();
    }
}

So you can easily call the Callback function (and you can extract runtime-passed parameters in OnSuccess method:

public override void OnSuccess(MethodExecutionArgs args)
{
    CallBack(GetValueYouNeed(args));
}

Simple version of PostSharp is a free-licence distributed and well documented, so I strongly suggest you to investigate this approach.

VMAtm
  • 27,943
  • 17
  • 79
  • 125
  • Looks promising, but it did'nt work for me. I inherited the class mentioned, overwrite the method `OnEntry` and nothing happened... the method didn't fired automatically. Perhaps I'm doing something wrong. – Yerko Palma Apr 22 '15 at 16:01
  • As I remember, you have to add a post build event for PostSharp do the wrapping – VMAtm Apr 22 '15 at 20:20