从c#中的方法返回值或错误消息的最佳做法是什么?

时间:2015-04-21 09:43:33

标签: c# return

我正在尝试从c#中的函数/方法中找到最简单的返回值或错误消息的解决方案。

现在我试过了:

public float ValidateValue (float value)
{
    if (value == VALID_VALUE)
    {
        return value;
    }
    else 
    {
        throw new ArgumentException("Invalid value", "value"); 
    }
}

这个解决方案似乎已经足够好了,但我在Clean Code Cheap Sheet找到了:

  

使用控制流的例外 - 不要这样做

     

使用控制流的例外:   表现不好,难以理解,结果很难   处理真实例外情况。

4 个答案:

答案 0 :(得分:4)

如果您想要验证某些输入值,我希望会返回bool,表示“有效”'或者'无效',或者没有返回值,当值无效时抛出异常。

所以我建议使用它:

public bool ValidateValue(float value)
{
    return value == VALID_VALUE;
}

或者这个:

public void ValidateValue(float value)
{
    if (value != VALID_VALUE)
    {
        throw new ArgumentException("Invalid value", "value"); 
    }
}

因此抛出异常不是问题,特别是当有多种理由拒绝时,您想区分各种原因。否则,只需使用bool,例如int.TryParse

答案 1 :(得分:3)

如果输入无效,您会怎么做?

如果您正在从用户输入的UI级别编写代码,那么执行以下操作最有意义:

private bool IsValid(float value)
{
  return value == VALID_VALUE; // replace with real check.
}

然后在调用代码中你会得到:

public void ReactToInput()
{
  float value = HoweverYouGetTheFloatFromTheUser();
  if(!IsValid)
  {
    //Code to display error message.
  }
  else
  {
    //Code to do something useful.
    //
    //Code to display result.
  }
}

因为你在这个级别的工作是"采取用户给我的东西,尽可能地回报他们想要的东西"在这个层面上,最好有可能让用户做一些不正确的前端和中心。

如果您正在编写其他代码的代码以供使用,那么最合适的做法是:

private void CheckValid(float valid)
{
  if(value != VALID_VALUE) // replace with real check.
    throw new ArgumentException();
}

然后在调用代码中你会得到:

public float DoWork(float value)
{
  CheckValid(value)
  //Code to do something useful.
  //
  //Code to return result.
}

在这里,你的工作是干净地完成方法的任务并返回一个有意义的结果(如果没有,则为void)。如果你不能做这项工作,因为你给出的输入是无稽之谈(或任何其他原因),那么你需要尽快停止并处理这个问题。你可以通过每次返回一个错误/成功代码并且每次调用代码来检查它来做到这一点,但是虽然这种方法确实有一些优点,但是例外让我们:

  1. 重点关注正确的行为。
  2. 传递例外情况。
  3. 例如1,比较:

    private bool WithExceptions()
    {
      return A() > B() && C() > D();
    }
    
    private bool WithExplicitChecks(out bool result)
    {
      result = false;
      int a;
      int b;
      if(!A(out a))
        return false;
      if(!B(out b))
        return false;
      if(a <= b)
        return true;
      int c;
      int d;
      if(!C(out c))
        return false;
      if(!D(out d))
        return false;
      result = c > d;
      return true;
    }
    

    对于2的示例,请考虑:

    private void A()
    {
      if(_someField == null)
        throw new InvalidOperationException("field not ready");
      _someField.DoSomething();
    }
    private void B()
    {
      A();
    }
    private void C()
    {
      B();
    }
    private string D()
    {
      try
      {
        C();
      }
      catch(InvalidOperationException)
      {
        Console.Error.WriteLine("Was not ready");
      }
    }
    

    显然,一个真实的案例会B()C()执行更多操作,但我们可以在此处看到只有A()必须担心提出异常而且只有D()关于交易与他们一起,B()C()都可以专注于主要问题。*

    这两种方法可以混合使用。考虑:

    private static string CheckValid(string path)
    {
      if(path.Length == 0)
        return "You cannot enter an empty file path";
      switch(path[path.Length - 1])
      {
        case '\\':
        case '/':
          return "You cannot enter a directory path";
      }
      return null;
    }
    public static void Main(string[] args)
    {
      Console.WriteLine("Enter a file path");
      var path = Console.ReadLine().Trim();
      var validationError = CheckValid(path);
      if(validationError != null)
        Console.Error.WriteLine(validationError);
      else
      {
        try
        {
          using(var reader = new StreamReader(path))
            Console.WriteLine(reader.ReadToEnd());
        }
        catch(FileNotFoundException)
        {
          Console.Error.WriteLine("File not found");
        }
        catch(UnauthorizedAccessException)
        {
          Console.Error.WriteLine("Access denied");
        }
        catch(IOException ioe)
        {
          Console.Error.WriteLine(string.Format("I/O Exception: {0}", ioe.Message));
        }
      }
      Console.Read();
    }
    

    这个简单的程序从用户获取文件路径,并打开相关文件并以文本形式输出内容。它采用两种错误处理方法。

    因为我们可以轻松检查空的无效输入,或以/\结尾的无效输入,这是通过简单的控制流来完成的,我们会提供错误消息而不是做某事。

    我们只能通过尝试打开文件并失败来了解其他问题,因此在这些情况下我们会处理异常。我将两种类型问题的显式检查结合起来,并将其与一类问题结合起来,并采取相应的行动。

    这里有第三种异常处理方式;如果发生了我根本不期望的异常,程序将失败并转发异常消息以进行调试。在任何你不能捕获所有异常的情况下都是如此,但这是非常有用的;因为我没有毯子catchcatch(Exception)我不会混淆我期望处理的异常(让我处理它们!),例外情况是因为我没有意识到他们可能会发生错误(嘘我!现在我必须解决它)。

    这是一个简单的程序,它从用户获取文件路径,并输出文件的内容。请注意,它结合了两种方法:

    *尽管如此,如果异常通过它,可能会始终认为某个方法中的某些内容可能无法完成。

答案 2 :(得分:1)

元组可能对于解决该问题很有帮助:

public Tuple<float,string> ValidateValue (float value)
if (value == VALID_VALUE)
{
    return new Tuple<bool, string>(value,string.Empty);
}
else 
{
    return new Tuple<bool, string>(false,"Invalid value"); 
}

调用函数时,请先检查错误字符串是否为空:

var validation = ValidateValue(myFloatValue);
if (validation.Item2 != string.Empty)
{
    // report error
}
else
{
    // No error core here validation.Item1 is your result
}

答案 3 :(得分:0)

一个想法可能是拥有一些通用模型。你可能有一些模型大致如下:

public class MyReturnModel
{
    public bool Success { get; set; }
    public string ErrorOrSuccessMessage { get; set; }
    public dynamic AnyModelToReturn { get; set; }

}

现在让我们将此应用于您提供的案例:

public MyReturnModel ValidateValue(float value)
{
    //function logic here 
    bool result = value == VALID_VALUE;
    string msg = result ? "valud is valid" : "value is invalid";
    return new MyReturnModel { Success = result, ErrorOrSuccessMessage = msg }
}