Diver Dan Diver Dan - 2 months ago 7
C# Question

Building a generic c# function that allows a function to be passed in as a parameter

I have a very ugly piece of code that is scattered throughout a project. The only difference in this piece of code is one line where a different method is called. The method that's called always returns a

bool
.

I want to refactor this and extract it into its own method and pass the 1 liner into this method (if possible) from my understanding I can use a
Func<>
to do this.

Here is what I am trying to do. I have tried to keep things as clear as possible

public async Task<bool> SomeMethod()
{
//code removed for readability.

//IsCustomerComplete will return a bool
var process = await RepeatableMasterPiece(1, 2, _myRepo.IsCustomerComplete(someParameterRequired));

//do something with process result
return process;
}

private async Task<bool> RepeatableMasterPiece(int param1, int param2, Func<Task<bool>> method)
{
int retry = 0;
bool soapComplete = false;
string soapFault = "just a placeholder for example";
bool blackListStatus = false;
while (!soapComplete && retry <= 1)
{
try
{
if (soapFault != null)
{
//do some stuff with param1 & param2 here
}
if (!soapComplete)
{
return await method.Invoke();
}
}
catch (FaultException ex)
{
soapFault = ex.Message;
retry++;
if (retry > 1)
{
throw ex;
}
}
}
}


From the repo

public async Task<bool> IsCustomerComplete(int id)
{
...removed other code here
return true;
}


Does this make sense, am I on the right track, from the examples I have found they only show
Funcs<>
passing in a
string
or
int
which makes things look a whole lot simpler.

Answer

Here is an example based on your provided details.

public async Task SomeMethod() {
    //code in method.

    var _myRepo = new repo();
    var someParameterRequired = 1;
    var process = await RepeatableMasterPiece(1, 2, () => _myRepo.IsCustomerComplete(someParameterRequired));

    //do something with process result
}

private async Task<bool> RepeatableMasterPiece(int param1, int param2, Func<Task<bool>> method) {
    int retry = 0;
    bool soapComplete = false;
    string soapFault = "just a placeholder for example";
    bool blackListStatus = false;
    while (!soapComplete && retry <= 1) {
        try {
            if (soapFault != null) {
                //do some stuff with param1 & param2 here
            }
            if (!soapComplete && method != null) {
                return await method();
            }
        } catch (FaultException ex) {
            soapFault = ex.Message;
            retry++;
            if (retry > 1) {
                throw ex;
            }
        }
    }
    return false;
}

The assumption here is that all the target methods will be returning Task<bool>

If the target function does not require any parameters then you can do as mentioned in other answer and just provide the function itself without the parenthesis.