views:

78

answers:

5

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!

A: 

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
+1  A: 

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

http://www.castleproject.org/dynamicproxy/index.html

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.

Stuart Lange
I second this. See my reply.
Henrik
A: 

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
A: 

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 _inner;

  public DynamicProxy(object inner)
  { 
      _inner = inner;
  }

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

    return returnValue;
  }   
}

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
+1  A: 

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: http://stackoverflow.com/questions/559656/help-and-information-about-aspect-oriented-programming/559722#559722

(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
Henrik
Thanks, that's exactly what I was looking for!
mr.b