-1

I try to implement a decorator pattern for handling error in database transactions. I have no problem with standard Func and Actions, but i have difficulties with functions having out parameter.

Here many topics with same question, and i figured out to implement my own delegate:

    public delegate TResult FuncWithOut<T1, T2, TResult>(T1 arg1, out T2 arg2);         

1) But i don't found how to implement method based on this delegate:

    private void SafetyExecuteMethod(Action action)
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            // Some handling
        }
    }

    private T SafetyExecuteFunction<T>(Func<T> func)
    {
        T result = default(T);
        SafetyExecuteMethod(() => result = func.Invoke());
        return result;
    }

    private SafetyExecuteFunctionWithOut // ??
    {
        // ??
    }

2) And how to call this method:

    public bool UserExists(string name)
    {
        return SafetyExecuteFunction(() => _innerSession.UserExists(name));
    }

    public void CreateUser(string name, string password)
    {
        SafetyExecuteMethod(() => _innerSession.CreateUser(name, password));
    }

    public bool CanUpdateUser(string userName, out string errorMessage)
    {
        // ??
        // _innerSession.CanUpdateUser(userName, out errorMessage);
    }

1 Answers1

0

Just use the same scheme as in your example of SafetyExecuteFunction<T>(Func<T> func).

One thing you have to pay attention to is that you need to use a temporary local variable for the out parameter.

private TResult SafetyExecuteFunctionWithOut<T1, T2, TResult>(FuncWithOut<T1, T2, TResult> func, T1 arg1, out T2 arg2)
{
    TResult result = default(TResult);
    T2 arg2Result = default(T2); // Need to use a temporary local variable here 

    SafetyExecuteMethod(() => result = func(arg1, out arg2Result));

    arg2 = arg2Result; // And then assign it to the actual parameter after calling the delegate.
    return result;
}

Calling the function does then work like this:

public bool CanUpdateUser(string userName, out string errorMessage)
{
    bool result = SafetyExecuteFunctionWithOut<string, string, bool>(_innerSession.CanUpdateUser, userName, out errorMessage);
    return result;
}

Note, that you have to pass _innerSession.CanUpdateUser as a parameter to SafetyExecuteFunctionWithOut instead of using a lambda expression.


Using the naive attempt:

private TResult SafetyExecuteFunctionWithOut<T1, T2, TResult>(FuncWithOut<T1, T2, TResult> func, T1 arg1, out T2 arg2)
{
    TResult result = default(TResult);

    SafetyExecuteMethod(() => result = func(arg1, out arg2));

    return result;
}

creates the error message:

CS1628 Cannot use ref or out parameter 'arg2' inside an anonymous method, lambda expression, or query expression

Why you are not allowed to do that is explained in this answer.

NineBerry
  • 26,306
  • 3
  • 62
  • 93