8

I'm looking for RealProxy replacement in .NET Core, and this issue forwards me to DispatchProxy.

It has simple API, but it's unclear, how to wrap existing object into proxy.
E.g., having this interface:

interface IFoo
{
    string Bar(int boo);
}

and this implementation:

class FooImpl : IFoo
{
    public string Bar(int boo)
    {
        return $"Value {boo} was passed";
    }
}

how to get what I want?

class Program
{
    static void Main(string[] args)
    {
        var fooInstance = new FooImpl();
        var proxy = DispatchProxy.Create<IFoo, FooProxy>();

        var s = proxy.Bar(123);

        Console.WriteLine(s);
    }
}

class FooProxy : DispatchProxy
{
    protected override object Invoke(MethodInfo targetMethod, object[] args)
    {
        return targetMethod.Invoke(/* I need fooInstance here */, args);
    }
}

Since DispatchProxy descendants must have parameterless constructor, the only idea I have is to invent some method, like this:

class FooProxy : DispatchProxy
{
    private object target;

    public void SetTarget(object target)
    {
        this.target = target;
    }

    protected override object Invoke(MethodInfo targetMethod, object[] args)
    {
        return targetMethod.Invoke(target, args);
    }
}

and use it this way:

var fooInstance = new FooImpl();
var proxy = DispatchProxy.Create<IFoo, FooProxy>();

((FooProxy)proxy).SetTarget(fooInstance);

// the rest of code...

Is this correct approach?

Dennis
  • 37,026
  • 10
  • 82
  • 150
  • The cast to FooProxy above won't compile because the type of proxy is IFoo. Declaring proxy to be of type object will solve the problem – robertfriberg Aug 25 '17 at 22:19
  • @robertfriberg: It *will* compile. This was pasted directly from sample, which compiles and runs as expected. – Dennis Aug 28 '17 at 05:53
  • Doesn't compile for me, see https://twitter.com/robertfriberg/status/903527168403275784 – robertfriberg Sep 01 '17 at 08:00
  • `DispatchProxy.Create()` return an `IFoo`, the compiler won't allow a cast too FooProxy. I'm using VS2017, C# 7.0, and .Net Standard 1.6 – robertfriberg Sep 01 '17 at 08:06
  • @robertfriberg: ah, I see. Our samples are not identical. The reason is that your code uses generics. While cast above is allowed for non-generic code, generic one requires double cast (my production code is generic one and contains double cast too). But in the context of a question, code from above is OK. – Dennis Sep 01 '17 at 08:54
  • I have an answer that I've supplied to a very similar question here: https://stackoverflow.com/a/54607410/2648798 - it might help to see a working implementation of DispatchProxy? – Robert Perry Feb 09 '19 at 15:32

2 Answers2

6

You are right that there is no other option here than to cast the generated IFoo to the known proxy type (FooProxy) and use a custom method or property on FooProxy. There is no public API to add constructor arguments or return the proxy as the implementation type. However, DispatchProxy.Create() will return an instance of a subclass of FooProxy whose type is generated at runtime via reflection and IL emitting.

If you are looking at other ways to quickly wrap an implementation and replace interface methods / virtual methods, I suggest using mocking frameworks instead (FakeItEasy, Moq, NSubstitute etc.).

Martin Ullrich
  • 94,744
  • 25
  • 252
  • 217
  • Thanks for the answer. I'm using proxies for some sort of extended logging. There's no goal to replace implementation. I just need to have ability to turn on/off parameters and return values logging for particular instance, and call original implementation. – Dennis Jul 05 '17 at 10:46
  • So the cleanest way would probably be to add sth like a `Logger` and `TargetInstance` property on your custom dispatch class if you want to use `DispatchProxy`. – Martin Ullrich Jul 05 '17 at 10:56
  • fyi the source code is here: https://github.com/dotnet/corefx/tree/master/src/System.Reflection.DispatchProxy/src/System/Reflection – Martin Ullrich Jul 05 '17 at 10:58
3

You need to create your own Generic class that inherit from DispatchProxy and has own static Create that has an extra parameter from type target.

example

public class AopAction<T>:DispatchProxy
{
  #region Private Fields
  private Action<MethodInfo,object[],object> ActAfter;
  private Action<MethodInfo,object[]> ActBefore;
  private Action<MethodInfo,object[],Exception> ActException;
  private T Decorated;
  #endregion Private Fields

  #region Public Methods
  public static T Create(T decorated,Action<MethodInfo,object[]> actBefore = null,Action<MethodInfo,object[],object> actAfter = null,Action<MethodInfo,object[],Exception> actException = null)
  {
    object proxy = Create<T,AopAction<T>>();
    SetParameters();
    return (T)proxy;
    void SetParameters()
    {
      var me = ((AopAction<T>)proxy);
      me.Decorated = decorated == null ? throw new ArgumentNullException(nameof(decorated)) : decorated;
      me.ActBefore = actBefore;
      me.ActAfter = actAfter;
      me.ActException = actException;
    }
  }
  #endregion Public Methods

  #region Protected Methods
  protected override object Invoke(MethodInfo targetMethod,object[] args)
  {
    _ = targetMethod ?? throw new ArgumentException(nameof(targetMethod));

    try
    {
      ActBefore?.Invoke(targetMethod,args);
      var result = targetMethod.Invoke(Decorated,args);
      ActAfter?.Invoke(targetMethod,args,result);
      return result;
    }
    catch(Exception ex)
    {
      ActException?.Invoke(targetMethod,args,ex);
      throw ex.InnerException;
    }
  }
  #endregion Protected Methods
}

to use your example

var proxy=AopAction<IFoo>.Create(new FooImpl());
Emre
  • 144
  • 2
  • 17
Waleed A.K.
  • 1,596
  • 13
  • 13