Gotham Llianen
Gotham Llianen

Reputation: 604

c# how to 'return if not null' in a one liner?

Is there a one liner to return something if it not null or continue execution, or how to make such thing? All this to avoid copy pasta the IF lines in several methods.

Initial code would be this:

var error = ValidateStuff(someArg);
if (error != null)
{
    return error;
}
DoOtherStuff();

So how to refactor it in order to avoid copy pasting that same if everywhere? Pseudo code would be something like

ValidateStuff(someArg) ? return ___ : continue;
DoSomethingElse();
AndMoreStuff();

-EDIT- An even more simplistic example, in order to clear some doubts going on in some answers and comments:

public string Foo(string arg)
{
    string fuu = GetMeSomething(arg);

    if(fuu != null) return fuu;

    ImDoingThings();

    return "I did things";
}

Would be awesome to have this:

public string Foo(string arg)
{
    ReturnIfNotNull(GetMeSomething(arg));

    ImDoingThings();

    return "I did things.";
}

Upvotes: 15

Views: 21839

Answers (4)

Ðаn
Ðаn

Reputation: 10875

First, you're likely going about this in the wrong way; you're probably going to be much better off throwing exceptions rather than trying to return error codes.

But ... you can make something like your pseudo-code work using lambdas. It gets messy (and would be even messier with generics), but for completeness and answering your specific question:

static Error ReturnIf(Error error,
    Func<Error, bool> predicate,
    Func<Error> rest)
{
    return predicate(error) ? error : rest();
}

static Error Test2(bool someArg)
{
    return ReturnIf(ValidateStuff(someArg), error => error != null, () =>
    {
        DoSomethingElse();
        AndMoreStuff();
        return null;
    });
}

Or, since you've actually said "return if not null", the code an be simplified and more specific:

static Error ReturnIfNotNull(Error error,
       Func<Error> rest)
{
    return error ?? rest();
}

static Error Test2(bool someArg)
{
    return ReturnIfNotNull(ValidateStuff(someArg) () =>
    {
        DoSomethingElse();
        AndMoreStuff();
        return null;
    });
}

I'm not suggesting you would actually want to use code such as this in your particular situation ... Although Bart de Smet explored this technique extensively.

Upvotes: 1

Heinzi
Heinzi

Reputation: 172220

In newer C# versions, you can use pattern matching for this. Instead of

var error = ValidateStuff(someArg);
if (error != null)
{
    return error;
}

you can write:

if (ValidateStuff(someArg) is var error)
{
    return error;
}

which can, if you want, be condensed into a single line:

if (ValidateStuff(someArg) is var error) return error;

(Note though that, unfortunately, error's scope extends beyond the if statement.)

Upvotes: 0

atlaste
atlaste

Reputation: 31106

Sure:

void ValidateStuff(someArg) { 
    if (!validation(someArg)) { 
        throw new ValidationException("Whatever went wrong...", errorDetails);
    }
}

And in your code:

ValidateStuff(someArg);
DoOtherStuff();

P.S.: I often combine generic code in ValidateStuff with #if (DEBUG) [...] #else [...] #endif, so that production irrelevant stuff doesn't end up in production binaries.


What about warnings?

I use a few tricks for that:

  1. Build an error object only if you really need it.
  2. Similarly, build the list of errors only when something fails.
  3. Make use of 'using' for easy coding. I'm a lazy coder... This is a small risk though; if you forget to use using, you're in trouble... Still, I think this risk is better than the alternative of "let's go on with the show and forget there was something warning in the first place".
  4. If you have a better handler for your warnings (so: instead of the exception), use it and be done with it.
  5. If an error occurs, throw the lot.

Obviously you can extend it as you see fit...

Without further due:

public class WarningsHandler : IDisposable
{
    private List<WarningErrorBase> errors = null;

    // Default handler. Remember to use 'using', or otherwise you'll end up 
    // with pain and suffering!
    public void Dispose()
    {
        var errors = FetchValidationResults();

        if (errors != null && errors.Count > 0)
        {
            throw new ValidationException(errors);
        }
    }

    // Handler if you have a better idea than using an Exception
    public IEnumerable<Error> FetchValidationResults() 
    {
        var errors = this.errors;
        this.errors = null;
        return errors;
    }

    public void Warn(bool condition, Func<Warning> errorBuilder)
    {
        if (condition) 
        { 
            if (errors == null) { errors = new List<WarningErrorBase>(); }
            errors.Add(errorBuilder()); 
        }
    }

    public void Error(bool condition, Func<Error> errorBuilder)
    {
        if (condition) 
        { 
            if (errors == null) { errors = new List<WarningErrorBase>(); }
            errors.Add(errorBuilder()); 

            throw new ValidationException(FetchValidationResults());
        }
    }
}

How to use it?

void MyThing()
{
    using (var handler = new WarningsHandler())
    {
        handler.Error(foo == null, "Foo must have a value");
        handler.Warn(foo.Count > 2, () => new Warning("You should have less than 2 foo's present.");
        // etc.
    }
} 

Okay, just one more trick. :-)

A last way to mix different error messages with little overhead is to use yield return. This enables you to return multiple result values with different behaviors. Null values can be ignored trivially.

First we need a whole bunch of wrappers for this:

// We need some base interface that we can use for return values
public interface IResult { }

// We have to wrap normal return values
public class Result<T> : IResult
{
    public Result(T result) { this.Value = result; }

    public T Value { get; private set; }
}

// A few classes for messages, errors, warnings, ...
public class Message : IResult
{
    public Message(string format, params object[] args)
    {
        this.Text = string.Format(format, args);
    }

    public string Text { get; private set; }

    internal virtual void Handle(List<Message> messages)
    {
        messages.Add(this);
    }
}

public class Error : Message
{
    public Error(Exception ex) :
        base("Uncaught exception: {0}", ex.Message)
    { }

    public Error(string format, params object[] args) : 
        base(format, args)
    { }

    internal override void Handle(List<Message> messages)
    {
        throw new ValidationException(this.Text);
    }
}

// Other wrappers like warnings, etc. 
// Wrapping IEnumerable<IResult> is useful too.

Next, we need some helper method to execute our methods that now return an IEnumerable instead of a normal type. For that, I add a helper class, which basically handles the execution, unwrapping and return values.

public static class ExecutionEngine
{
    public static T Execute<T>(this IEnumerable<IResult> method)
    {
        List<Message> messages = new List<Message>();
        try
        {
            foreach (var item in method)
            {
                // yield return null is ignored here:
                if (item != null)
                {
                    // Handle validation results, messages, etc
                    Message msg = item as Message;
                    if (msg != null)
                    {
                        msg.Handle(messages);
                    }

                    Result<T> returnValue = item as Result<T>;
                    if (returnValue != null)
                    {
                        return returnValue.Value;
                    }

                    // handle other things, error if something's wrong
                }
            }

            throw new Exception("Method finished without a return value.");
        }
        catch (ValidationException)
        {
            // TODO: handle messages?

            throw;
        }
        catch (Exception ex)
        {
            // TODO: handle messages?

            var error = new Error(ex);
            error.Handle(messages);

            throw; // unreachable because Error throws. This is to make sure it all compiles
        }
    }
}

Once we have all that in order, the code itself becomes pretty simple, and resembles a lot like you would do normally. The main difference is that you simply add 'yield return' everywhere, sometimes with an additional wrapper:

public IEnumerable<IResult> MyMethod()
{
    // Delegate validation to somewhere else. You might wrap an IEnumerable<IResult> here:
    yield return ValidateStuff(someArg);

    // Information messages, etc
    yield return new Message("Hello world!");

    // You might end up with an Exception you didn't expect...
    var tmp = new List<int>();
    tmp[2] = 2; // oopz...

    // ...
    yield return new Result<int>(12); // return 12;
}

The only thing remains is that you cannot call MyMethod anymore. This is easily fixed by using the execution engine:

int result = MyMethod().Execute<int>();

Upvotes: 12

Moradof
Moradof

Reputation: 483

I think the best way to validate is throw an custom exception. (CustomException is my own class that inherited from Exception class)

for example, I use this in my business layer: (save model in database or something else)

public class SomeService
{

    public void DoSomething(some params or some instance of a model class)
    {
        Validate(some params or an instance of a model class);

        // some other codes such as save data in database or etc ...

    }

    private void Validate(some params or an instance of a model class)
    {
        if(something is wrong)
            throw new CustomException("X is invalid"); // X is the name of wrong param
    }

}

and in presentation layer:

try
{
   var model = new someModel{ fill properties of model };

   var service = new SomeService();
   service.DoSomething(model);

   DoOtherStuff();
}
catch(CustomException ex)
{
  ShowMessageToUser(ex.Message);
}

Note: I catch just my custom exceptions, not business exceptions.

It's my dear war for validate parameters and models

I hope this helps.

Upvotes: 0

Related Questions