0

Is there any way to dynamically intercept method calls in a class in C#, equivalent to the Perl AUTOLOAD mechanism?

Case in point, I have a helper class with a 'core' method that writes to the system Event Log and a couple of convenience overloads to simplify the most common uses.

Now, I am seeing an emerging code pattern where I use try ... catch to attempt to write an entry, but ignore any failures that are related to the actual event log handling. For instance when trying to log an application exception when the event log is full, I want the application to crash with the "real" application exception, not the "event log" exception.

I have currently just created a new set of overloads that encapsulates this, but what I would really like to do is have dynamic handling of these methods, i.e. any method call to a method name starting with "Try" calls the respective "real" method, encapsulated in a try .. catch. This is would be so easy in Perl ;-) but can it even be done in C#?

Some code that might simplify the explanation:

public class SomeClass
{
    // Core functionality
    public static void WriteToLog(string message, EventLogEntryType type)
    {
        ...
    }

    // Overloaded methods for common uses
    public static void WriteToLog(SomeObject obj)
    {
        WriteToLog(obj.ToString(), EventLogEntryType.Information);
    }

    public static void WriteToLog(SomeException ex)
    {
        WriteToLog(ex.Message, EventLogEntryType.Error);
    }

    // Additional wrappers that ignores errors
    // These are what I'd like to handle dynamically instead of manually:
    public static void TryWriteToLog(SomeObject obj)
    {
        try 
        {
            WriteToLog(obj);
        }
        catch (Exception logException)
        {
            Console.WriteLine(logException.Message);
        }
    }

    public static void TryWriteToLog(SomeException ex)
    {
        try 
        {
            WriteToLog(ex);
        }
        catch (Exception logException)
        {
            Console.WriteLine(logException.Message);
        }
    }
}
Christoffer
  • 12,712
  • 7
  • 37
  • 53
  • possible duplicate of [How do I intercept a method call in C#?](http://stackoverflow.com/questions/25803/how-do-i-intercept-a-method-call-in-c) – Jamiec Sep 06 '11 at 13:51
  • @Jamiec not exactly, since I am interested in redirecting the function call, not only logging it. – Christoffer Sep 06 '11 at 14:58
  • Figured out an answer on my own, but I'll leave the question open for now in case anyone comes up with a better solution. – Christoffer Sep 06 '11 at 15:15

1 Answers1

0

Oh... Much to my surprise, I figured it out over a cup of coffee and it actually works. To paraphrase the initial code snippet, here's what I did:

using System;
using System.Dynamic;
using System.Reflection;

public class SomeClass : DynamicObject
{
    // Core functionality
    public static void WriteToLog(string message, EventLogEntryType type)
    {
        ...
    }

    // Overloaded methods for common uses
    public static void WriteToLog(SomeObject obj)
    {
        WriteToLog(obj.ToString(), EventLogEntryType.Information);
    }

    public static void WriteToLog(SomeException ex)
    {
        WriteToLog(ex.Message, EventLogEntryType.Error);
    }

    // Redirect all method calls that start with 'Try' to corresponding normal
    // methods, but encapsulate the method call in a try ... catch to ignore
    // log-related errors

    private static dynamic instance = new SomeClass();

    public static dynamic Instance { get { return instance; } }

    public override bool TryInvokeMember(InvokeMemberBinder binder, 
                                         object[] args, 
                                         out object result)
    {
        if (binder.Name.StartsWith("Try"))
        {
            try
            {
                result = this.GetType().InvokeMember(binder.Name.Substring(3),
                                                     BindingFlags.InvokeMethod, 
                                                     null, 
                                                     this,
                                                     args);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.InnerException.Message);
                result = null;
            }
            return true;
        }
        else
        {
            return base.TryInvokeMember(binder, args, out result);
        }
    }

The following methods can now be invoked, and seems to work as intended:

SomeClass.Instance.WriteToLog(SomeObject obj)
SomeClass.Instance.TryWriteToLog(SomeObject obj)
SomeClass.Instance.WriteToLog(SomeException ex)
SomeClass.Instance.TryWriteToLog(SomeException ex)
SomeClass.Instance.WriteToLog(string message, EventLogEntryType type)
SomeClass.Instance.TryWriteToLog(string message, EventLogEntryType type)

Small caveat: The above code is cleaned up for posting on an official forum, it might not work out of the box.

Christoffer
  • 12,712
  • 7
  • 37
  • 53