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.";
}
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.
In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.
Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.
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:
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>();
First, you're likely going about this in the wrong way; you're probably going to be much better off throw
ing 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With