从c#中的方法返回值或错误消息的最佳实践是什么?
我试图找到最干净的解决方案,从c#中的函数/方法返回值或错误消息 目前,我已尝试:从c#中的方法返回值或错误消息的最佳实践是什么?,c#,return,C#,Return,我试图找到最干净的解决方案,从c#中的函数/方法返回值或错误消息 目前,我已尝试: public float ValidateValue (float value) { if (value == VALID_VALUE) { return value; } else { throw new ArgumentException("Invalid value", "value"); } } 这个解决方案似乎足够好,
public float ValidateValue (float value)
{
if (value == VALID_VALUE)
{
return value;
}
else
{
throw new ArgumentException("Invalid value", "value");
}
}
这个解决方案似乎足够好,但我发现:
对控制流使用异常–不要这样做
对控制流使用异常:
表现不好,难以理解,结果很难理解
处理真正的例外情况
如果要验证某个输入值,我希望返回一个
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
就可以了。一个想法可能是建立一些通用模型。您可能有一些大致如下的模型:
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 }
}
如果输入无效,您会怎么做 如果您是在UI级别编写代码,从用户那里获取输入,那么执行以下操作最有意义:
private bool IsValid(float value)
{
return value == VALID_VALUE; // replace with real check.
}
private void CheckValid(float valid)
{
if(value != VALID_VALUE) // replace with real check.
throw new ArgumentException();
}
然后在调用代码中,您将有:
public void ReactToInput()
{
float value = HoweverYouGetTheFloatFromTheUser();
if(!IsValid)
{
//Code to display error message.
}
else
{
//Code to do something useful.
//
//Code to display result.
}
}
public float DoWork(float value)
{
CheckValid(value)
//Code to do something useful.
//
//Code to return result.
}
因为在这个级别上,你的工作是“拿走用户给我的东西,尽我所能归还他们想要的东西”,在这个级别上,最好是让用户在前面和中间做一些不正确的事情
如果您正在为其他代码编写代码以供使用,那么最好执行以下操作:
private bool IsValid(float value)
{
return value == VALID_VALUE; // replace with real check.
}
private void CheckValid(float valid)
{
if(value != VALID_VALUE) // replace with real check.
throw new ArgumentException();
}
然后在调用代码中,您将有:
public void ReactToInput()
{
float value = HoweverYouGetTheFloatFromTheUser();
if(!IsValid)
{
//Code to display error message.
}
else
{
//Code to do something useful.
//
//Code to display result.
}
}
public float DoWork(float value)
{
CheckValid(value)
//Code to do something useful.
//
//Code to return result.
}
在这里,您的工作是干净利落地完成方法的任务,并返回一个有意义的结果(如果没有,则返回void
)。如果你不能做那项工作,因为你得到的信息是胡说八道的(或者其他原因),那么你需要尽快停止工作,解决这个问题。您可以通过每次返回一个错误/成功代码并让调用代码每次检查来做到这一点,但是尽管这种方法确实有一些优点,但例外情况让我们:
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;
}
显然,一个真实的案例需要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();
}
这个简单的程序从用户那里获取一个文件路径,打开相关的文件并将内容输出为文本。它采用两种方法来处理错误
因为我们可以很容易地检查空的无效输入,或者以/
或\
结尾的无效输入,这是通过简单的控制流完成的,我们会显示错误消息,而不是执行某些操作
我们只能通过尝试打开文件并失败来了解其他问题,因此在这些情况下,我们会处理异常。我将两类问题的显式检查与一类一般问题的显式检查结合起来,并相应地采取行动
这里有第三种类型的异常处理;如果发生了我完全没有预料到的异常,程序将失败,并转储异常消息以进行调试。在任何地方都是这样,你不能捕捉到所有的异常,但这是一个非常有用的异常;因为我没有一套完整的catch
或catch(Exception)
我不会把我期望处理的异常(让我来处理它们!)与存在的异常混淆,因为我犯了一个错误,没有意识到它们可能会发生(糟糕!现在我必须修复它)
这是一个简单的程序,从用户那里获取文件路径,并输出文件内容。请注意,它结合了两种方法:
*尽管总是认为在一个方法中启动的东西如果一个异常通过它可能无法完成。< /P> < P>可能有助于解决这个问题:
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");
}
查看
int.Parse
和int.TryParse
。只要您通常期望输入有效,异常就可以了。从逻辑上讲,这意味着导致调用检查model.Success&&model.anymodeltorereturn.Success&&model.anymodeltorereturn.anymodeltorereturn.Success
,以了解使用model.anymodeltorereturn.anymodeltorereturn.anymodeltorereturn
是否安全。由于所有的动态属性
的存在,这也将不必要地使用大量反射。在某些地方,这样的方法可能有价值,但这结合了异常的缺点和显式成功检查的缺点,并增加了反射的缺点。。。。。。这是假设你从一开始就知道要把这些东西拆开多远。@JonHanna你说得对。可能是我很快就看到了另一面并解释了。我把它取下来。感谢anywas先生:)对于某些情况,这当然是一种合理的方法,在这些情况下,确实需要进行非例外验证并生成模型。(尽管如此,如果可以的话,我还是会键入anymodel来返回
,而不是使用dynamic
)。您的代码有缺陷:value
是float
,但您将其作为第一个参数分配给Tuple
。