【问题标题】:function that returns two different types返回两种不同类型的函数
【发布时间】:2022-01-24 14:05:18
【问题描述】:

我应该有一个函数必须返回错误的string(通过try / catch)或不同的类型 T

示例这样的功能:

public T get()
{
  T struttura;

  try {
    ...
  }
  catch (Exception xcp) {
    return xcp.Message;
  }
  
  ...
   
  return struttura;
}

【问题讨论】:

  • 为什么不直接抛出异常,让调用者处理呢?
  • out 而不是 return ?
  • 为什么只返回部分异常?如果不打算处理异常,为什么要捕获异常?这不会使代码更可靠——恰恰相反。异常包含的不仅仅是一条消息——它包含的内部异常可能包含比根异常更多的信息、一个调用堆栈,它显示导致异常的调用链以及异常的位置被抛出
  • 同意,只要抛出异常,让调用方法处理即可。仅仅返回异常消息是没有意义的,因为你会错过调用堆栈和任何内部异常。
  • 有一些方法可以返回结果或错误,但它们需要仔细编码,而不仅仅是忽略错误。这通常会导致比让异常传播到可以处理的级别更复杂的代码。在 Go 中,函数通常返回一个带有结果或错误的元组。 Go 代码 always 在使用结果之前检查是否有错误。等效使用(T,Exception) 作为返回类型。在 F# 和 Rust 中,使用了 Result<TResult,TError> 类型。在F#中,成功和失败用不同的类型表示,防止代码使用无效结果

标签: c# exception


【解决方案1】:

种方法可以做到这一点,但请认真考虑这是否是您真正想要的。让异常向上冒泡进入调用代码几乎总是更好。

第一种方法是使用out 参数。

public string get(out T result)
{
 T struttura;
 try{...}
 catch (Exception xcp)
 {
  result = default(T);
  return xcp.Message;
 }
 ...
 result = struttura;
 return String.Empty;
}

第二种方法是使用ValueTuple

public (T, string) get()
{
 T struttura;
 try{...}
 catch (Exception xcp){return (default(T), dexcp.Message);}
 ...
 return (struttura, string.Empty);
}

【讨论】:

  • 正确处理细节会很痛苦——例如,如何在成功路径中返回默认值?你如何防止调用者忽略错误?最好将错误类型设为可空以强制调用者检查值,尤其是当结果是数字或结构时。由于错误隐藏代码,我看到系统很高兴地返回 0 作为美元/欧元汇率
【解决方案2】:

.net 设计指南https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exception-throwing 建议永远不要将异常作为返回类型返回。抛出错误并在调用者中捕获总是更好的设计。

指南还建议,如果您不想抛出错误,可以遵循 TryParse 模式https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions-and-performance#try-parse-pattern。通常,您提供两种方法,Get 和 TryGet。 Try 方法的存在应该向调用者表明 Get 将引发异常,但 TryGet 不会。如果操作成功,TryGet 还会返回一个布尔值,允许您在不使用调用方的 try/catch 块的情况下处理否定情况。

【讨论】:

    【解决方案3】:

    我建议TryGet签名:

    public bool TryGet(out T struttura) {
      try {
        ...
        struttura = ...
        ...
    
        return true;
      }
      catch (Exception xcp){
        struttura = default(T);
    
        return false; 
      }
    }
    

    用法:

    if (TryGet(out var myStruct)) {
      // succeeded, myStruct is returned struttura
    }
    else {
      // failed
    }
    

    或者根本不捕获异常,或者重新抛出异常作为自定义异常:

    public T Get() {
      try {
        ...
        return struttura;
      }
      catch (Exception xcp) {
        throw new MyException("My message", xcp);
      }
    }
    

    用法:

    try {
      myStruct = Get();
    }
    catch (MyException e) {
      // Failed, e.Message for message
      Console.WriteLine(e.Message);
    }
    

    最后,您可以机械地组合valuemessage并返回命名元组

    public (T value, string message) Get() {
      try {
        ...
        return (struttura, null);
      }
      catch (Exception xcp) {
        return (default(T), xcp.message);
      }
    }
    

    用法:

    var result = Get();
    
    if (result.message == null) {
      // succceded with result.value
    }
    else {
      // failed with result.message
    }
    

    【讨论】:

      猜你喜欢
      • 2021-11-19
      • 1970-01-01
      • 1970-01-01
      • 2012-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-18
      • 1970-01-01
      相关资源
      最近更新 更多