Reputation: 30205
I have many methods that require some logging with the same pattern. Some methods need to return some value, some don't. I have created a method with Action parameter to avoid copypasting all of the logic. It looks like this:
private void Execute(Action action)
{
Logger.Start();
try
{
action();
}
catch(Exception exception)
{
Logger.WriteException();
throw;
}
finally
{
Logger.Finish();
}
}
Now I have some calls that like that
public void DoSomething(string parameter)
{
Execute(() => GetProvider(parameter).DoSomething());
}
But I need some function that return values. What are the best ways to do it? I have found two now:
1) Create a copy of Execute method with Func
private T Execute<T>(Func<T> action)
{
Logger.Start();
try
{
return action();
}
catch(Exception exception)
{
Logger.WriteException();
throw;
}
finally
{
Logger.Finish();
}
}
This method works but has some copy paste as well.
2) Trick the parameter into being an Action:
public Result DoSomething(string parameter)
{
Result result = null;
Execute(() => result = GetProvider(parameter).DoSomething());
return result;
}
This does not require copy paste but does not look so nice.
Is there a way to join Action and Func somehow to avoid any of these methods or may be there is another way to achieve the same result?
Upvotes: 8
Views: 1393
Reputation: 149020
Create a copy of Execute
that converts the Func
into an Action
. You only have to write that ugly code once, and you don't end up with a complete second copy of the Execute
method:
private T Execute<T>(Func<T> func)
{
T result = default(T);
this.Execute(() => { result = func(); });
return result;
}
...
public Result DoSomething(string parameter)
{
return Execute(() => GetProvider(parameter).DoSomething());
}
Upvotes: 3
Reputation: 31071
Here's another option. Instead of having the logging framework call your actual code, have your actual code call the logging framework. Something like this would do the trick (greatly simplified).
public class LoggerScope : IDisposable {
private bool disposed;
public LoggerScope() {
Logger.Start();
}
public void Dispose() {
if(!disposed) {
Logger.Finish();
disposed = true;
}
}
}
Used as follows:
using(var scope = new LoggerScope()) {
// actual code goes here
}
Handle exceptions separately by catching and logging them only once at the top level of your code.
Advantages:
LoggerScope
class, e.g. GUID, timestamp, logical task description text.Upvotes: 2
Reputation: 1500385
A third option is to still overload Execute
, but make the Action
version work in terms of the Func
version:
private void Execute(Action action)
{
// We just ignore the return value here
Execute(() => {
action();
return 0;
});
}
Of course all this would be simpler if void
were more like a "real" type (like Unit
in F# et al), at which point we could just have Task<T>
instead of Task
and Task<T>
as well...
Upvotes: 7