3

Edit: Changed question title from "Does C# allow method overloading, PHP style (__call)?" - figured out it doesn't have much to do with actual question. Also edited question text.

What I want to accomplish is to proxy calls to a an instance of an object methods, so I could log calls to any of its methods.

Right now, I have code similar to this:

class ProxyClass {
    static logger;
    public AnotherClass inner { get; private set; }
    public ProxyClass() { inner = new AnotherClass(); }
}

class AnotherClass {
    public void A() {}
    public void B() {}
    public void C() {}
    // ...
}

// meanwhile, in happyCodeLandia...
ProxyClass pc = new ProxyClass();
pc.inner.A(); // need to write log message like "method A called"
pc.inner.B(); // need to write log message like "method B called"
// ...

So, how can I proxy calls to an object instance in extensible way? Method overloading would be most obvious solution (if it was supported in PHP way). By extensible, meaning that I don't have to modify ProxyClass whenever AnotherClass changes.

In my case, AnotherClass can have any number of methods, so it wouldn't be appropriate to overload or wrap all methods to add logging.

I am aware that this might not be the best approach for this kind of problem, so if anyone has idea what approach to use, shoot.

Thanks!

mr.b
  • 4,932
  • 11
  • 38
  • 55
  • 1
    What is it you're really trying to do though? Could it be solved using params (http://msdn.microsoft.com/en-us/library/w5zay9db(VS.71).aspx)? – Tom Jun 05 '10 at 14:34
  • As I wrote above, basically I want to write messages to log each time method that exists in "inner" is called. – mr.b Jun 05 '10 at 14:39
  • @Tom: Yes, it's close to what I need. – mr.b Jun 05 '10 at 14:49

5 Answers5

2

I have use a number of solutions to not quite this problem, but similar things.

1- You could derive a custom proxy from RealProxy and take advantage of the call interceptio provided by the .NET remoting infrastructure. The advantage is that this is super easy, but the limitation is that you need to proxy either an interface and use reflection to invoke the members of your inner class or the class being proxied must inherit from MarshalByRrefObject.

While I hesitate to give a code sample, just because it would not be complete, and I will probably get flamed. But here is a ten minute piece of code just to get you pointed to the right classes etc. Beware, I only handle the IMethodCallMessage so this is not complete but should work as a demo.

class LoggingProxy<T> : RealProxy where T : MarshalByRefObject, new()
{
  T _innerObject;

  public static T Create()
  {
    LoggingProxy<T> realProxy = new LoggingProxy<T>();
    T transparentProxy = (T)realProxy.GetTransparentProxy();
    return transparentProxy;
  }

  private LoggingProxy() : base(typeof(T))
  {
    _innerObject = new T();
  }

  public override IMessage Invoke(IMessage msg)
  {
    if (msg is IMethodCallMessage)
    {
      IMethodCallMessage methodCall  = msg as IMethodCallMessage;

      System.Diagnostics.Debug.WriteLine("Enter: " + methodCall.MethodName);
      IMessage returnMessage = RemotingServices.ExecuteMessage(_innerObject, msg as IMethodCallMessage);
      System.Diagnostics.Debug.WriteLine("Exit: " + methodCall.MethodName);
      return returnMessage;
    }

    return null;
  }
}

This can be used as follows

class MyClass : MarshalByRefObject
{
  public int Age
  {
    get;
    set;
  }
}

MyClass o = LoggingProxy<MyClass>.Create();
o.Age = 10;

The above will log the call to set_Age on the proxied instance of MyClass.

2- Another alternative, but much more work is to create a proxy class that dynamically generates a type derived from the type you pass in and provides implementations of all the methods and properties in the base type. The generated methods etc. will perform the logging call the base class implementation etc. similar to the RealProxy example. Using VS var type you can probably avoid the need to actually inherit from the type and rather use aggregation for this proxy, that way you will still have intelli-sense support and not need to make all the methods/properties virtual. Sorry no example, this is a little too much for now. But you can look at using CodeDom or better yet Reflection.Emit for the dynamic type building. The dynamic code could do something like that proposed in @tvanfosson's answer.

3- And finally you cpuld use DynamicObject to do much of the above, the disadvantage is that you will not have compile time verification of method calls and no intelli-sense. Again, here is a minimal example.

public class DynamicProxy : System.Dynamic.DynamicObject
{
  private object _innerObject;
  private Type _innerType;

  public DynamicProxy(object inner)
  {
    if (inner == null) throw new ArgumentNullException("inner");
    _innerObject = inner;
    _innerType = _innerObject.GetType();
  }

  public override bool TryInvokeMember(System.Dynamic.InvokeMemberBinder binder, object[] args, out object result)
  {
    System.Diagnostics.Debug.WriteLine("Enter: ", binder.Name);

    try
    {
      result = _innerType.InvokeMember(
        binder.Name,
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod,
        null, _innerObject, args);
    }
    catch (MissingMemberException)
    {
      return base.TryInvokeMember(binder, args, out result);
    }
    finally
    {
      System.Diagnostics.Debug.WriteLine("Exit: ", binder.Name);
    }

    return true;
  }

  public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
  {
    System.Diagnostics.Debug.WriteLine("Enter: ", binder.Name);

    try
    {
    result = _innerType.InvokeMember(
      binder.Name,
      BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty,
      null, _innerObject, null);
    }
    catch (MissingMemberException)
    {
      return base.TryGetMember(binder, out result);
    }
    finally
    {
      System.Diagnostics.Debug.WriteLine("Exit: ", binder.Name);
    }

    return true;
  }    

  public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
  {
    System.Diagnostics.Debug.WriteLine("Enter: ", binder.Name);

    try
    {
    _innerType.InvokeMember(
      binder.Name,
      BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
      null, _innerObject, new object[]{ value });
    }
    catch (MissingMemberException)
    {
      return base.TrySetMember(binder, value);
    }
    finally
    {
      System.Diagnostics.Debug.WriteLine("Exit: ", binder.Name);
    }      

    return true;
  }

  public override string ToString()
  {
    try
    {
      System.Diagnostics.Debug.WriteLine("Enter: ToString");
      return _innerObject.ToString();
    }
    finally
    {
      System.Diagnostics.Debug.WriteLine("Exit: ToString");
    }
  }
}

Which is used something like the following

dynamic o2 = new DynamicProxy(new MyClass());
o.Age = 10;

There are a few option for rolling your own solution, alternatively you can look at some of the pre-backed solutions. Which is probably a better way to go, but this should give you some insight as to how some of the solutions have possibly been implemented.

Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
  • You're not using `inner` in that `TryInvokeMember`. I don't think that will work. – Jordão Jul 15 '12 at 00:18
  • @Jordão - Thank you. You are correct, the DynamicProxy sample was incomplete and incorrect. I have added a more complete version, but it has not been robustly tested, it should however give you an idea as to the approach. – Chris Taylor Jul 15 '12 at 08:37
2

Echoing the two other; DI is the way to go. Dynamic Proxy is very competent in this respect.

Here is some example code, complete with implementation of all that is required. Normally it's good practice to code against an interface.

I can recommend reading a bit about AOP, here's my thread on it: Help and Information about Aspect Oriented Programming

(Edit: Becuase you were nice and gave me some points, here's another cool link to a DP tutorial that's really well done: http://kozmic.pl/archive/2009/04/27/castle-dynamic-proxy-tutorial.aspx ;))

Here is example code:

using System;
using System.Collections.Generic;
using Castle.Core;
using Castle.Core.Interceptor;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using NUnit.Framework;

[TestFixture]
public class LoggingMethodInvocationsTests
{
    [Test]
    public void CanLogInvocations()
    {
        var container = new WindsorContainer();
        container.Register(Component.For<LoggingInterceptor>().LifeStyle.Singleton);
        // log all calls to the interface
        container.Register(Component.For<IA>().ImplementedBy<A>().Interceptors(typeof (LoggingInterceptor)));

        var a = container.Resolve<IA>();
        a.AMethod(3.1415926535); // to interface
        Console.WriteLine("End of test");
    }
}

public class LoggingInterceptor : IInterceptor, IOnBehalfAware
{
    private string _entityName;

    public void Intercept(IInvocation invocation)
    {
        var largs = new List<string>(invocation.Arguments.Length);

        for (int i = 0; i < invocation.Arguments.Length; i++)
            largs.Add(invocation.Arguments[i].ToString());

        var a = largs.Count == 0 ? "[no arguments]" : string.Join(", ", largs.ToArray());
        var method = invocation.Method == null ? "[on interface target]" : invocation.Method.Name;

        Console.WriteLine(string.Format("{0}.{1} called with arguments {2}", _entityName, method, a));

        invocation.Proceed();

        Console.WriteLine(string.Format("After invocation. Return value {0}", invocation.ReturnValue));
    }

    public void SetInterceptedComponentModel(ComponentModel target)
    {
        if (target != null)
            _entityName = target.Implementation.FullName;
    }
}

public class A : IA
{
    public double AMethod(double a)
    {
        Console.WriteLine("A method impl");
        return a*2;
    }

    public void SecondMethod(double a)
    {
        Console.WriteLine(string.Format("Impl: SecondMethod called with {0}", a));
    }
}

public interface IA
{
    double AMethod(double a);
}

Console output

Examples.A.AMethod called with arguments 3,1415926535
A method impl
After invocation. Return value 6,283185307
End of test
Community
  • 1
  • 1
Henrik
  • 9,714
  • 5
  • 53
  • 87
1

This may be too heavyweight for your particular use case, but you may want to look into Castle Dynamic Proxy:

Dynamic Proxy

This framework allows you to dynamically create proxies for your classes at runtime, allowing you to intercept all calls and inject whatever logic you want.

DeadlyChambers
  • 5,217
  • 4
  • 44
  • 61
Stuart Lange
  • 4,049
  • 6
  • 24
  • 30
0

The way I would go about it is using dependency injection and passing the logger instance to the class that needs to do the logging. If you had a framework, like MVC, that supports attribute-based filtering, then you could use those as well, though you might be limited in what you could log.

public class LoggedClass
{
     private Logger Logger { get; set; }

     public LoggerClass( Logger logger )
     {
          this.Logger = logger;
     }

     public void A()
     {
         this.Logger.Info( "A has been called" );
         ...
     }
}

Or in MVC, or a suitable framework that understands attributes and can invoke them before method calls.

[Log]
public ActionResult A()
{
   ...
}

public class LogAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
         ... use context to log stuff
    }
}

The last alternative, I can think of, which you've already said you don't want to use, is the Decorator pattern. In this case your proxy class and the proxied class would need to implement the same interface and you'd simply wrap the proxied class with the functionality that you wanted. Note that defining the interface -- and extending it when you need to add functionality to the logged class -- protects you from forgetting to extend your proxy to keep it in synch with the logged class. Since it implements the interface it won't compile unless it has all the interface methods.

public interface IDoSomething
{
    void A();
    void B();
}

public class ToLogClass : IDoSomething
{
    public void A() { ... }
    public void B() { ... }
}

public class LoggedClass : IDoSomething
{
    private IDoSomething Inner { get; set; }
    private Logger Logger { get; set; }

    public Proxy( IDoSomething inner, Logger logger )
    {
        this.Inner = inner;
        this.Logger = logger;
    }

    public void A()
    {
        this.Logger.Info( "A callsed on {0}", this.Inner.GetType().Name );
        this.Inner.A();
    }

}
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
0

Another option is an Aspect-Oriented framework like PostSharp:

http://www.sharpcrafters.com/

This allows you to define attributes that inject code that will be called at certain point during the method call (OnEntry, OnExit, OnException, etc.).

The big downside of this tool is that it requires you to run a post-compilation step against your binaries (the injection is not done dynamically at runtime, but during this post-compile step).

Stuart Lange
  • 4,049
  • 6
  • 24
  • 30