Click here to Skip to main content
12,944,121 members (55,079 online)
Rate this:
Please Sign up or sign in to vote.
See more:
Hi everybody,

I'm looking for a an elegant way to invoke methods of a certain interface from a wrapper, so that the wrapper (or its base class) has access to the method signature and the value of the argument. Lambda expressions should be used in order to support intellisense (like interface => interface.DoSomething).

This way the wrapper (TestWrapper) is kept as simple as possible and any additional functionality for all methods can be implemented at a single point in the base class of the wrapper (WrapperBase<tinterface></tinterface>) (e.g. caching, logging, processing of arguments or results etc).

This interface is implemented and wrapped by the class TestWrapper. Imagine that there are some other interfaces that have to work with the same generic base class WrapperBase<TInterface> and should not require special base classes.
public interface ITest
    int GetLength(string text);
    FooResponse GetFoo(GetFooRequest r);
    BarResponse GetBar(GetBarRequest r);
    // lots of other method signatures

Wrapper base class
The delegate Method is required to pass a method without its arguments. The implementation of TInterface is injected by the inheriting class TestWrapper.
public delegate TResult Method<TArg, TResult>(TArg arg);
public class WrapperBase<tinterface> where TInterface : class
    private TInterface _value;
    // ...
    protected TResult Invoke<targ,>(
        Func<TInterface, Method<targ,>    {
        // method and argument are required for
        //  execution, caching, logging, other processing...

Wrapper implementation
This class implements ITest and just delegates all method calls to its base class, where additional functionality for all methods is implemented at a single point instead of every method of TestWrapper.
This class can be used like var testWrapper = new TestWrapper(new TestImplementation())
public class TestWrapper : WrapperBase<ITest>, ITest
    public TestWrapper(ITest value) : base(value) { }
    public int GetLength(string text)
        // type arguments cannot be derived
        // return Invoke(p => p.GetLength, text);
        // works fine
        return Invoke<string, int>(p => p.GetLength, text);
    // many other methods...

As shown in the above code snippet only the second statement is accepted by the compiler (with type arguments). The line above throws an error, because the type arguments can not be derived.

1) My first solution of WrapperBase used
TResult Invoke<TResult>(Func<TInterface, TResult> command)
and could be called by TestWrapper with base.Invoke(t => t.GetLength(text)) without any type arguments.
The only disadvantage was that the Invoke method had no access to the argument value (here text), so the argument could not be used for caching, validation etc.

2) My second solution used
public TResult Invoke<TResult>(Expression<Func<TInterface, TResult>> command).
It could be called the same way as the first solution (without any type arguments) and allowed access to the parameter. The disadvantage of this solution is that the expression has to be compiled before it can be executed. I really don't want to use Compilation and Reflection for every single method call.

Can the signature of the Invoke method be changed, so that the type arguments can be deducted correctly?

Any ideas are welcome!

1) The editor messed up the declaration of the Invoke method; this was fixed.
2) Other methods were added to ITest to show that all result and argument types are different.
Posted 17-May-11 10:33am
Updated 18-May-11 0:13am

1 solution

Rate this: bad
Please Sign up or sign in to vote.

Solution 1

Just Check My Implementation

public interface ITest
       int GetLength(string text);
       // many other methods...

The interface has GetLength method.

   public class TestClass<TInterface> where TInterface : ITest
       protected TResult Invoke<TArg, TResult>(Func<TInterface, int> method, TArg arg)
           return default(TResult);

TestClass takes an input of ITest and stores it as Type argument when object is created. From Invoke Method I am using this interface. We intentionally made Func returntype as int as we are going to call getLength later.

  public class WrapperITest : ITest
       #region ITest Members
       public int GetLength(string text)
           return text.Length;

Implementation of ITest which will have GetLength method defined. You specified return type as int, so it has strict rule on Type argument

public class TestWrapper : TestClass<ITest>
     public int GetLength(string text)
         return Invoke<string, int>(p =>  p.GetLength(text), text);

I hope this will help.
glFrustum 18-May-11 5:44am
Thanks for your suggestion, but I think you assume that all methods of ITest return int. If the methods of ITest have n different types for TResult, you would need n different Invoke methods. I updated the code of ITest to point that out.

The goal was to use just one Invoke method for all Func<TArg, TResult> without explicit type arguments like Invoke(p => p.GetLength, text).
Abhishek Sur 18-May-11 13:58pm
That is very simple, use T as Return type argument rather than explicit int. Also remember, if you are to specify any type as Func return type in Invoke other than Targs and TResult, you need to specify it here as well.
glFrustum 18-May-11 19:14pm
Its only simple if you pass the parameter twice :-) I would be very happy to get rid of (request) behind c.GetFoo, but can't find the solution.

public FooDto GetFoo(GetFooRequest request) {
return Invoke(c => c.GetFoo(request), request);

In order to avoid the duplicate parameter, the type arguments must be specified, but this isn't very elegant, too.

public FooDto GetFoo(GetFooRequest request) {
return Invoke<FooDto, GetFooRequest>(c => c.GetFoo, request);

It looks like I would have to decide between these two variants...
nissims 31-Aug-13 5:36am
I'm sorry but i don't understand what you are trying to do.
can you send me a reproduce code (just copy your code to a console app including the part that don't work)
I have deal with that kind of problems in the past i want to be sure that this is the case in order to give you the correct answer (if this is the situation)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

    Print Answers RSS
Top Experts
Last 24hrsThis month
OriginalGriff 4,653
CHill60 2,970
Maciej Los 2,388
Jochen Arndt 1,900
ppolymorphe 1,765

Advertise | Privacy | Mobile
Web02 | 2.8.170518.1 | Last Updated 18 May 2011
Copyright © CodeProject, 1999-2017
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100