如果它不为空或继续执行,或者如何制作这样的东西,是否有一个衬垫返回一些东西?这一切都是为了避免在几种方法中复制面食中的IF线.
初始代码是这样的:
var error = ValidateStuff(someArg);
if (error != null)
{
return error;
}
DoOtherStuff();
Run Code Online (Sandbox Code Playgroud)
那么如何重构它以避免复制粘贴,如果无处不在?伪代码就像是
ValidateStuff(someArg) ? return ___ : continue;
DoSomethingElse();
AndMoreStuff();
Run Code Online (Sandbox Code Playgroud)
-EDIT-一个更简单的例子,为了澄清一些答案和评论中的疑虑:
public string Foo(string arg)
{
string fuu = GetMeSomething(arg);
if(fuu != null) return fuu;
ImDoingThings();
return "I did things";
}
Run Code Online (Sandbox Code Playgroud)
拥有这个会很棒:
public string Foo(string arg)
{
ReturnIfNotNull(GetMeSomething(arg));
ImDoingThings();
return "I did things.";
}
Run Code Online (Sandbox Code Playgroud)
atl*_*ste 12
当然:
void ValidateStuff(someArg) {
if (!validation(someArg)) {
throw new ValidationException("Whatever went wrong...", errorDetails);
}
}
Run Code Online (Sandbox Code Playgroud)
在你的代码中:
ValidateStuff(someArg);
DoOtherStuff();
Run Code Online (Sandbox Code Playgroud)
PS:我经常在结合通用的代码ValidateStuff与#if (DEBUG) [...] #else [...] #endif,使生产不相关的东西并不在生产二进制文件结束.
怎么警告?
我用了一些技巧:
显然你可以按照你认为合适的方式扩展它......
没有进一步到期:
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());
}
}
}
Run Code Online (Sandbox Code Playgroud)
如何使用它?
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.
}
}
Run Code Online (Sandbox Code Playgroud)
好的,还有一个技巧.:-)
混合不同错误消息的最后一种方法是使用yield return.这使您可以返回具有不同行为的多个结果值.空值可以忽略不计.
首先,我们需要一大堆包装器:
// 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.
Run Code Online (Sandbox Code Playgroud)
接下来,我们需要一些辅助方法来执行我们现在返回IEnumerable而不是普通类型的方法.为此,我添加了一个辅助类,它基本上处理执行,解包和返回值.
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
一旦我们按顺序完成所有这些,代码本身就变得非常简单,就像你通常会做的很像.主要区别在于您只需在任何地方添加"yield return",有时会使用额外的包装器:
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;
}
Run Code Online (Sandbox Code Playgroud)
唯一剩下的就是你不能再打电话MyMethod了.使用执行引擎可以很容易地解决这个问题:
int result = MyMethod().Execute<int>();
Run Code Online (Sandbox Code Playgroud)