1
\$\begingroup\$

I want to add an error reporting feature to my WPF app. I have the view for it created; I just need to know when to call it. I am used to web development and this is my first WPF app.

public static void Get<T>(Func<T> func, Action<BaseResult<T>> callback, bool report = false){

    BaseResult<T> result;

    try { 

        result = new BaseResult<T>(func()); 
    } 

    catch (Exception ex){

        if (report) new ErrorWindow(ex.Message).Show();

        result = new BaseResult<T>(false, ex.Message);

        
    }

    callback(result);
}

The BaseResult class stores the result if the call was successful and an error message if it isn't. Sometimes I don't want to report the result of the call to an error screen, sometimes I just want to know if the call was successful.

  1. Does this violate Single Responsibility Principle?

  2. This can't be the best way of implementing this. Has anyone done this before? My way seems overly complicated.

\$\endgroup\$
1
  • \$\begingroup\$ composition per se does not violate SRP. In fact composition is a natural result of good SRP across the app class' design. Pass-through methods for composed-object methods is also SRP compliant. However implementing things that are rightfully composed-object functionality is SRP violation, even if least knowledge principle integrity is maintained for client code. \$\endgroup\$
    – radarbob
    Commented Sep 28, 2023 at 21:41

1 Answer 1

1
\$\begingroup\$

Let's split your logic into two methods

public static void Get<T>(Func<T> func, Action<BaseResult<T>> callback, bool report = false)
{
    var result = GetSafe(func, report);

    if (report && result.IsFault) 
    {
        new ErrorWindow(ex.Message).Show();
    }
        
    callback(result); //does not fit here nicely
}

private static BaseResult<T> GetSafe<T>(Func<T> func)
{
    try 
    { 
        return new(func()); 
    } 
    catch (Exception ex)
    {
        return new(false, ex.Message);
    }
}
  • The GetSafe's responsibility is to execute the provided user delegate and map the execution outcome accordingly
  • The Get's responsibility is to call GetSafe and notify user about failure

And that's where the callback does not fit in.

Change return type

public static BaseResult<T> Get<T>(Func<T> func, bool report = false)
{
    var result = GetSafe(func, report);

    if (report && result.IsFault) 
    {
        new ErrorWindow(ex.Message).Show();
    }

    return result;
}

private static BaseResult<T> GetSafe<T>(Func<T> func)
{
    try 
    { 
        return new(func()); 
    } 
    catch (Exception ex)
    {
        return new(false, ex.Message);
    }
}

With this approach the consumer of the Get API does not have to provide an "execution outcome handler" rather than they can do the handling by their own.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thank you this is very helpful. \$\endgroup\$ Commented Sep 29, 2023 at 10:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.