Click here to Skip to main content
15,867,686 members
Please Sign up or sign in to vote.
5.00/5 (4 votes)
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).

Interface
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.
C#
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.
C#
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...
    }
}</tinterface>


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())
C#
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...
}


Problem
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.

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

Any ideas are welcome!
Thanks

Update
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
Updated 18-May-11 0:13am
v4

1 solution

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

     #endregion
 }


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.
 
Share this answer
 
Comments
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).
Nissim Salomon 31-Aug-13 5:36am    
Hi
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)
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...

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



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900