1

I'm on .NET 4.8, under Windows Forms. I have a specific amount of declared methods inside a project, in various classes. And I would like that every method in these classes to call another specific method at the very start of each method execution. What I mean it's like If I manually add the required code at line number one inside each method body to call this other specific method.

Why I would like to do this?: I wrote a specific method that prints debug information on which I'm interested to know. This method includes info about the caller member, but it is not in any way limited to that kind of info.

Then, while I'm developing any application in Debug mode I would like to call this method automatically on every other method call in my project (inside property getter/setter too, if possible), and I'm just very curious to know if exists an alternative to the nightmare of writing/copying a thousand times the required instruction to call this specific method inside each method body in the project.

Exists a way to achieve this at runtime?. Maybe... writing an helper method that would be called once to retrieve all declared methods through Reflection and do code injection points in their method bodies?. Not sure how to do so, and not sure if that is viable anyways; maybe with Reflection.Emit as suggested in this answer?

And could this be achieved without depending on third party dependencies like Postsharp as suggested in this question?. Really using Postsharp is not a viable solution for me because I suppose the solution will consist to decorate with custom attributes every declared method in the project.

I also found this other suggestion but it's basically what I intend to avoid: manually add code changes to every method call in the project (in this case to replace the invocation code).

Some suggestions that I found like that last are not applicable to my scenario because I need to call this specific method INSIDE / FROM the method body of other methods in order to be able retrieve debug info of the current caller member including its parameters and values.

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • 1
    [RealProxy](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.remoting.proxies.realproxy?view=netframework-4.8.1&WT.mc_id=DT-MVP-5003235) may help. – Reza Aghaei Jan 06 '23 at 03:10
  • 1
    Could couple with [Host startup hooks](https://github.com/dotnet/runtime/blob/main/docs/design/features/host-startup-hook.md#host-startup-hook) (see. Guidance and caveats) – Jimi Jan 06 '23 at 03:46
  • @Jimi "allows injecting managed code to run before the main application's entry point" - maybe code injection related things are too advanced for me. Anyways, that is only for .NET Core?, and it is capable to inject NET code to run only before the main entry point?, or it could also be used to modify the body of specific methods to inject managed code there?. --- "An example of such a use case is a hook that injects logging, telemetry, or profiling into an existing deployed application at runtime." - this use case sounds exactly as what I need. – ElektroStudios Jan 07 '23 at 05:55
  • @Jimi Its usage seems to be limited to .NET Core and its purpose limited to: "register globally a method in an assembly that will be executed whenever a .net core application is started." - Thankyou in any case! – ElektroStudios Jan 07 '23 at 06:06
  • @Jimi I'm not sure what could really achieve that LL-hook, if it is purely limited or not to run managed code before the main method / entry point, but I can always add and run a custom method at the 'Application.Startup' event in .NET Framework, then at first sight I think I don't require that LL-Hook in case of it only serves to do a similar thing like that, and I don't know if it is only limited to .NET Core. But this LL-Hook its a new thing that I discovered, thanks again. – ElektroStudios Jan 07 '23 at 06:14

1 Answers1

2

As an option, RealProxy may help. There's a nice article by Bruno Sonnino on this topic: Aspect-Oriented Programming : Aspect-Oriented Programming with the RealProxy Class.

Also you may be able to use AOP features of Unity (the DI framework) for this purpose. There's a nice article on this topic by Dino Esposito: Interceptors in Unity.

Using a weaving tool like Fody is also another option.

Creating proxy classes using Reflection.Emit could be another option.

Example - AOP using RealProxy

A LogAttribute which Has MethodExecuting and MethodExecuted and runs before and after methods

Using RealProxy, you can create a proxy for your class, so that when you call a method, the Invoke method of the proxy will run and you can run any logic there, for example you can run something before or after the actual method call.

In this example, I show how you can create a MethodFilterAttribute having two methods OnMethodExecuting and OnMethodExecuted, and then if you decorate your method with an attribute derived from this attribute, those methods will run before and after executing of the original method.

Looking into the code, you see you don't necessarily need the attributes, and attributes are just there as an extensibility point.

The usage of the code in this example is something like this:

var calc = CalculatorFactory.GetInstance();
var a = calc.Add(1, 2);
var b = calc.Subtract(1, 2);

Which produce the output:

Add executing.
Add executed.
Subtract executing.
Subtract executed.

Using statements

This is an attribute that could be used for methods. It has OnMethodExecuting and OnMethodExecuted methods, and when you get a proxy of your class, and run the methods, these two filter methods will be executed before and after the method which is decorated by this attribute:

using System;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Linq;

[AttributeUsage(AttributeTargets.Method)]
public class MethodFilterAttribute : Attribute
{
    public int Order { get; set; }
    public virtual void OnMethodExecuting(
        MethodInfo methodInfo, object[] args) { }
    public virtual void OnMethodExecuted(
        MethodInfo methodInfo, object[] args, object result) { }
}

LogAttribute

An implementation of MethodFilterAttribute which performs log before and after method execution:

public class LogAttribute : MethodFilterAttribute
{
    override public void OnMethodExecuting(
        MethodInfo methodInfo, object[] args)
    {
        Console.WriteLine($"{methodInfo.Name} executing.");
    }
    override public void OnMethodExecuted(
        MethodInfo methodInfo, object[] args, object result)
    {
        Console.WriteLine($"{methodInfo.Name} executed.");
    }
}

The DynamicProxy class

Creates a proxy of your object, and if you run methods of the object, if the methdod is decorated with a method filter attribute, then OnActionExecuting and OnActionExecuted will run.

Looking into the code, you see you don't necessarily need the attributes, and attributes are just there as an extensibility point.

public class DynamicProxy<T> : RealProxy
{
    private readonly T original;
    public DynamicProxy(T original)
      : base(typeof(T))
    {
        this.original = original;
    }
    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;
        var methodInfo = methodCall.MethodBase as MethodInfo;
        try
        {
            var filters = methodInfo.GetCustomAttributes<MethodFilterAttribute>();
            if (filters.Any())
            {
                filters.OrderBy(x => x.Order).ToList()
                 .ForEach(f => f.OnMethodExecuting(methodInfo, methodCall.InArgs));
            }
            var result = methodInfo.Invoke(original, methodCall.InArgs);
            if (filters.Any())
            {
                filters.OrderBy(x => x.Order).ToList()
                 .ForEach(f => f.OnMethodExecuted(methodInfo, methodCall.InArgs, result));
            }
            return new ReturnMessage(result, null, 0,
              methodCall.LogicalCallContext, methodCall);
        }
        catch (Exception e)
        {
            return new ReturnMessage(e, methodCall);
        }
    }
}

ICalculator interface and Calculator class

Methods of Calculator are decorated with Log attribute, which means before and after execution of those methods, log will run.

Please note: In the implementation of the proxy we are looking for the attributes, using the interface. Also having the interface is necessary.

public interface ICalculator
{
    [Log]
    int Add(int x, int y);
    [Log]
    int Subtract(int x, int y);
}

public class Calculator : ICalculator
{
    public int Add(int x, int y)
    {
        return x + y;
    }
    public int Subtract(int x, int y)
    {
        return x - y;
    }
}

CalculatorFactory

The factory which returns a proxy instance of ICalculator:

public class CalculatorFactory
{
    public static ICalculator GetInstance()
    {
        var original = new Calculator();
        return new DynamicProxy<ICalculator>(original)
            .GetTransparentProxy() as ICalculator;
    }
}

Usage

Get an proxied instance of the interface using the factory and run methods:

var calc = CalculatorFactory.GetInstance();
var a = calc.Add(1, 2);
var b = calc.Subtract(1, 2);

Which produce the output:

Add executing.
Add executed.
Subtract executing.
Subtract executed.
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • 1
    Please note: This is not production ready, and it's just for learning purpose or as a start point. Mixing this idea with a Dependency Injection frameworks makes it easier and nicer. – Reza Aghaei Jan 06 '23 at 04:14
  • Thanks for comment, and excuse me for this question but if I understood good using this 'RealProxy' approach is not possible to run method "A" while / from / inside method "B"?, instead and again if I understood good it will execute method "A" before and after method "B" execution. – ElektroStudios Jan 06 '23 at 04:22
  • You can run everything that you want, before and after your methods. What I've shared here, is just an example, showing an extensibility point on how to run something before and after each method, just by decorating the method with an attribute. Something like action filters in ASP.NET MVC. – Reza Aghaei Jan 06 '23 at 04:26
  • The RealProxy creates a proxy around your class, and everytime that you call a method of your class, `Invoke` method of the proxy will run. Then you can imagine the flexibility and you see you can do everything you want. – Reza Aghaei Jan 06 '23 at 04:27
  • But what I would like to know is to ensure that the RealProxy class can be used or not to indicate to run method "A" from / inside method "B", because my method "A" has a paramater with 'CallerMemberNameAttribute' class used to retrieve info from the caller method "B". I'm not requesting you to do the required modifications but just to ensure me that with this approach using RealProxy class I could achieve or not to do what I need. – ElektroStudios Jan 06 '23 at 04:51
  • And then I don't need to mess with 'IMessage' or other types that implements those related rare interfaces in 'System.Runtime.Remoting.Messaging' namespace?. Since your first comment on this topic I'm trying to figure how to define a 'IMessage' or 'IMethodCallMessage' or whatever it would be needed to pass a method call instruction to 'Invoke' method, and I can't find a single example due lack of documentation – ElektroStudios Jan 06 '23 at 04:54
  • 1
    Assuming you have method `A([CallerMemberName] name)`, if you call it inside method B, then it use `B` as `name`. Using `RealProxy`, inside the `Invoke` method of the proxy, you call it like `A(methodInfo.Name)`. – Reza Aghaei Jan 06 '23 at 04:54
  • ...but now I think its just that I'm having a wrong idea of the usage concept for RealProxy class, because it seems that I only would require to set some custom attributes and to modify the body of 'Invoke' method and not to touch the "IMessage" object neither to pass a custom 'Imessage' object to 'Invoke' method, it would be called automatically instead once the RealProxy class is instanced as I understand?. I need to check and try all this better. Thanks for your answer, examples and the help. – ElektroStudios Jan 06 '23 at 04:55
  • 1
    I think the Attribute example was too advanced, before you get any idea of the proxy. Ignore the custom attributes in my example, just use the proxy class, and instead of adding logic using the attributes, just include your custom logic in the Invoke method. Give it a try and see if it fits in your solution. – Reza Aghaei Jan 06 '23 at 04:57
  • One important point: Interfaces are necessary and if you do not have interfaces or you do not want to implement interfaces for your classes, you cannot use RealProxy. – Reza Aghaei Jan 06 '23 at 05:00
  • After testing it I concluded it does not fit my needs. The usage of a "proxy" requires to create a new object (new DynamicProxy), and use it instead of the actual object. This could be a tolerable downside or even a wanted behavior depending on the scenario, but in any case a "proxy" is not capable to do what I really would like to do (what I requested in my question, automate the call to a specific "X" method INSIDE / FROM the method body of every other declared method).I think my needs maybe would be more related to automate code injection and maybe use Reflection.Emit,a nightmare for me. – ElektroStudios Jan 07 '23 at 05:43
  • At the begining, this rare and unpopular and unfriendly class with that lack of documentation and its purpose had totally confused me a lot, and with that name and terminology, "proxy". But it's just a class on which you pass a type to its class constructor, so it creates a new instance of that type, and then the "Invoke" method is executed when you call the methods of that "proxied" type through this class. More or less it is something like that . Anyways it's cool to discover these kind of strange (not very popular) classes and what they can do. – ElektroStudios Jan 07 '23 at 05:43
  • Thanks for sharing your feedback. You know your requirement better, so choose whatever fits more in your solution; however, I can say in many similar cases, no matter how, creating proxy classes is the solution for these kind of problems. For example, EF also uses creating proxies to support lazy loading, or support notifying property changes. The library which is used in EF Core is [Castle.Core](https://github.com/castleproject/Core/blob/master/docs/dynamicproxy.md). – Reza Aghaei Jan 07 '23 at 12:02
  • 1
    I am curious to know what did you do to solve the problem :) – Reza Aghaei Mar 24 '23 at 00:02
  • 1
    I didn't solved it, I abandoned the idea of automating it and went back to the rudimentary way of doing things (add the method call in the methods I want at design time). – ElektroStudios Mar 24 '23 at 17:26