c#如何在一个班轮中"返回如果不为空"?

Got*_*nen 6 c#

如果它不为空或继续执行,或者如何制作这样的东西,是否有一个衬垫返回一些东西?这一切都是为了避免在几种方法中复制面食中的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,使生产不相关的东西并不在生产二进制文件结束.


怎么警告?

我用了一些技巧:

  1. 仅在您确实需要时才构建错误对象.
  2. 同样,只有在出现故障时才构建错误列表.
  3. 利用'使用'轻松编码.我是一个懒惰的程序员......虽然风险很小; 如果你忘记使用,你就会遇到麻烦......不过,我认为这种风险比"让我们继续演出并忘记首先发出警告"的选择更好.
  4. 如果你有一个更好的警告处理程序(所以:而不是异常),使用它并完成它.
  5. 如果发生错误,请抛出该批次.

显然你可以按照你认为合适的方式扩展它......

没有进一步到期:

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)

  • 这将OP指向正确的方向,即使用异常而不是"返回错误". (4认同)