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

Trz*_*cje 3 c# return

我试图找到最干净的解决方案,用于从 c# 中的函数/方法返回值或错误消息。

现在我已经尝试过:

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

这个解决方案似乎已经足够好了,但在我发现的清洁代码便宜表中

对控制流使用异常——不要这样做

对控制流使用异常:性能不佳,难以理解并导致对真实异常情况的处理非常困难。

Pat*_*man 5

如果您想验证某些输入值,我希望bool返回 a 来指示“有效”或“无效”,或者不返回值并在该值无效时抛出异常。

所以我建议使用这个:

public bool ValidateValue(float value)
{
    return value == VALID_VALUE;
}
Run Code Online (Sandbox Code Playgroud)

或这个:

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

所以抛出异常并不是问题,特别是当有多种拒绝原因,并且你想区分各种原因时。否则,只需使用 a bool,就像int.TryParse示例一样。


Jon*_*nna 5

在无效输入的情况下你会怎么做?

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

private bool IsValid(float value)
{
  return value == VALID_VALUE; // replace with real check.
}
Run Code Online (Sandbox Code Playgroud)

然后在调用代码中,您将拥有:

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

因为你在这个级别的工作是“接受用户给我的东西,尽我所能返回他们想要的东西”,在这个级别,最好让用户有可能在前面和中间做一些不正确的事情。

如果您正在为其他代码编写代码以供使用,那么执行以下操作最有意义:

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

然后在调用代码中,您将拥有:

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

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

  1. 写的重点是正确的行为。
  2. 传递异常。

对于 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;
}
Run Code Online (Sandbox Code Playgroud)

对于 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");
  }
}
Run Code Online (Sandbox Code Playgroud)

显然,一个真实的案件都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();
}
Run Code Online (Sandbox Code Playgroud)

这个简单的程序从用户那里获取文件路径,打开相关文件并将内容作为文本输出。它采用两种方法进行错误处理。

因为我们可以很容易地检查无效的输入是空的,或者以/or结尾的\,这是通过简单的控制流完成的,我们会显示一条错误消息而不是做一些事情。

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

这里有第三种异常处理;如果发生了我根本不希望发生的异常,程序就会失败,并为调试目的转储异常消息。在没有捕获所有异常的任何地方都是这种情况,但非常有用;因为我没有毯子,catch或者catch(Exception)我不会将我期望处理的异常(去我处理它们!)与那里的异常混淆,因为我犯了一个错误,没有意识到它们可能发生(嘘!现在我必须修复它)。

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

*尽管总是考虑到如果异常通过它,方法中启动的某些东西可能不会完成。