Mathieu
Mathieu

Reputation: 4520

How to evaluate why a method returns what it returns

What strategy do you use to give to the user the reason why a certain method "failed"

Exemple:

public List<Balance> GetBalanceFinale(Periode periode)
    {
        if (periode == null || periode.DateStart >= DateTime.Now || isBalanceFinished(periode.PeriodeID))
            return null;

        //My other code...
     }

I want to tell the user which of the steps went wrong. I don't want to use a messagebox in such class. I can't return the description of the failure because I already return something.

What do you usally do? Any advice? Thanks!

Upvotes: 3

Views: 200

Answers (8)

Damith
Damith

Reputation: 63105

You need to re-factor your code first. before calling GetBalanceFinale you can validate it and show proper message if validation failed. if validation pass you can call GetBalanceFinale method.

Sometimes you may not able to do all the validation before calling the method. in that case you can throw exception with proper message or use out parameters.

Upvotes: 2

Paul Hennessey
Paul Hennessey

Reputation: 203

An alternative that has worked for me in the past is the Notification pattern.

This is a way of getting information out of your domain layer and up into the presentation. For example, create something like this:

public class Notification
{
  public List<Message> Messages;
  public bool HasMessages;
  // etc
}

and use an instance of it as a property on your domain.

You can then do something like this:

myDomain.GetBalanceFinale(periode);
if(myDomain.Notification.HasMessages)
  // get the messages and do something with them

Upvotes: 2

Sean Thoman
Sean Thoman

Reputation: 7499

I am assuming you don't want to throw an exception otherwise you would've already done that. Something like an alert / warning without stopping execution of the program. In that case, you can still use an exception, just don't throw it, instead pass it as an out parameter or put it somewhere where the user can access it if desired. If that seems over the top then just use a message instead.

Also framing it as a 'Try' method might be a good idea. It makes it very clear that the method is prone to failure under certain conditions.

These are all different options:

public bool TryGetBalanceFinale(Periode periode, out List<Balance> list, out string msg)
{
    // return false if anything is wrong, and have an out parameter for the result & msg
}

public bool TryGetBalanceFinale(Periode periode, out List<Balance> list, out Exception ex)
{
    // return false if anything is wrong, and have an out parameter for the exception
}

These first two above are my two preferred approaches. The following are possibilities as well, however they are somewhat non-standard:

public Tuple<string, bool> TryGetBalanceFinale(Periode periode, out List<Balance> list)
{
    // return false if anything is wrong, and include message in the returned Tuple
}


// an anonymous type approach
public object TryGetBalanceFinale(Periode periode, out List<Balance> list)
{
    return new {
       Successful = false,
       Message = // reason why here
    };
}

// a functional approach
public List<Balance> list GetBalanceFinale(Periode periode, Action<String> messageAct)
{
   // when something is wrong, do:
   messageAct("Something went wrong..."); 
}

I think the 'Try' strategy makes the most sense when you consider how it will be used:

string message;
List<Balance> result;

if (!TryGetBalanceFinale(periode, out result, out message))
{
    // examine the msg because you know the method failed
    Console.WriteLine(message); 
}
else
{
    // you know the method succeeded, so use the result
    Console.WriteLine("The result is: " + result.ToString()); 
}

Upvotes: 2

Erik Philips
Erik Philips

Reputation: 54636

I like to wrap my results in a ResultState<T> object (usually for Json or Xml serialization). Might be helpful if you are building a framework for someone else to consume as each result can be handled the same way by the consumer.

public class ResultState<T>
{
  public T ResultValue { get; set; }
  public Exception ExceptionThrown { get; set; }
  public bool IsValid { get; set; }
  public string FriendlySummary  { get; set; }
  // whatever else properties you think are needed
}

public interface IResultState<T>
{
  public T ResultValue { get; }
  public Exception ExceptionThrown { get; }
  public bool IsValid { get; }
  public string FriendlySummary  { get; }
  // whatever else properties you think are needed
}

public IResultState<List<Balance>> GetBalanceFinale(Periode periode)
{
  ResultState<List<Balance>> result = new ResultState<List<Balance>>();
  try
  {
    if (periode == null 
        || periode.DateStart >= DateTime.Now 
        || isBalanceFinished(periode.PeriodeID))
    {
      result.IsValid = false;
      result.FriendlySummary = "Periode is in an invalid state.";
    }

    //My other code...

    result.ResultValue = new List<Balance>();
    result.ResultValue.Add(...);

  }
  catch(Exception ex)
  {
    result.IsValid = false;
    result.Exception = ex;
    // Ambigious is bad.. so for bad example..
    result.FriendlySummary = "An unknown exception happened.";    
  }
}

Upvotes: 2

Artak
Artak

Reputation: 2897

Consider throwing exceptions instead of returning null.

In this case you will be able to provide descriptive information with each exception, which later can be properly handled and presented to the caller.

Upvotes: 5

Glenn Ferrie
Glenn Ferrie

Reputation: 10408

You can decompose your logic into 3 separate tests, and then define an 'out' argument to return the "reason"

public List<Balance> GetBalanceFinale(Periode periode, out string reasonFailed)
{
    reasonFailed = false;
    if (periode == null)
    {  
       reasonFailed = "preiod is null";
       return null;
    }
    // etc.....
    //periode.DateStart >= DateTime.Now || isBalanceFinished(periode.PeriodeID))


    //My other code...
 }

Upvotes: 1

Brandon
Brandon

Reputation: 70022

If I need to return a value and a message, I just use an out parameter.

public List<Balance> GetBalanceFinale(Periode periode, out string errorMessage)
{
    if (periode == null) 
    {
       errorMessage = "Periode is null";
       return null;
    }

    // Other checks
 }

Then just call it like

string errorMessage;
var value = GetBalanceFinale(periode, out errorMessage);
if(value == null)
   // Do whatever with errorMessage

Upvotes: 1

SLaks
SLaks

Reputation: 888167

You can throw an exception with a descriptive message.

Upvotes: 7

Related Questions