【问题标题】:The best way to throw an exception抛出异常的最佳方法
【发布时间】:2014-05-23 18:13:28
【问题描述】:

你知道比下面更好的方法(更漂亮)来抛出异常吗?

public long GetPlaylistId(long songInPlaylistId)
{
    var songInPlaylist = service.GetById(songInPlaylistId);
    return songInPlaylist
            .With(x => x.Playlist)
            .ReturnValueOrException(x => x.Id, 
                                         new ArgumentException(
                                             "Bad argument 'songInPlaylistId'"));
}

一元扩展方法:

public static TResult With<TInput, TResult>(this TInput obj, 
                                            Func<TInput, TResult> evaluator)
    where TInput : class
    where TResult : class
{
    return obj == null ? null : evaluator(obj);
}

public static TResult ReturnValueOrException<TInput, TResult>(
    this TInput obj, Func<TInput, TResult> evaluator, Exception exception)
    where TInput : class
{
    if (obj != null)
    {
        return evaluator(obj);
    }

    throw exception;
}

【问题讨论】:

  • 不要使用异常来控制程序的流程。当没有其他选项可以解决问题时,您应该引发异常。 msdn.microsoft.com/en-us/library/dd264997.aspx
  • @Tim Schmelter,我认为在这种情况下我必须抛出异常。对于您的意见,我可以回报什么?
  • @Neshta:一种方法:返回Nullable&lt;long&gt;,另一种方法:TryGetPlaylistId 使用out 参数和bool 作为返回值。
  • 在您希望参数有效的情况下抛出异常是完全可以接受的。例如,如果文件不存在,使用new FileStream(filename, FileMode.Open); 会抛出FileNotFoundException 异常。
  • 如果建议的空传播运算符在该语言中,他可能(取决于其确切定义)return songInPlaylist?.Playlist?.Id;

标签: c# exception null monads


【解决方案1】:

如果我的班级中有多个这样的模棱两可的方法会发生什么?为任何方法发明不同的规则是非常困难的。你最终会感到困惑。 您如何看待这个解决方案?

public class ApplicationResponse
{
    public IList<string> Errors { get; set; }
    public dynamic Data { get; set; }

    public bool HasErrors()
    {
        return Errors != null && Errors.Any();
    }
}

public ApplicationResponse GetPlaylistId(long songInPlaylistId)
{
    var songInPlaylist = service.GetById(songInPlaylistId);
    if (songInPlaylist == null)
    {
        return new ApplicationResponse { Errors = new[] { "Song was not found." } };
    }

    if (songInPlaylist.Playlist == null)
    {
        return new ApplicationResponse { Errors = new[] { "Playlist was not found." } };
    }

    return new ApplicationResponse { Data = songInPlaylist.Playlist.Id };
}

public HttpResponseMessage SomeRequest([FromUri] songInPlaylistId)
{
    var response = appService.GetPlaylistId(long songInPlaylistId);
    if (response.HasErrors())
    {
        // reply with error status
    }

    // reply with ok status
}

在这种情况下,我可以将所有错误发送给客户端。

【讨论】:

    【解决方案2】:

    如果尝试获取没有播放列表的播放列表是有效的,那么您不应该抛出异常,而应该只返回一个表示“未找到”的特殊值(例如,0 或-1 取决于您的播放列表 ID 的工作方式)。

    或者,您可以编写一个TryGetPlaylistId() 方法,其工作方式类似于 Microsoft 的 TryXXX() 方法(例如 SortedList.TryGetValue()),例如:

    public bool TryGetPlaylistId(long songInPlaylistId, out long result)
    {
        result = 0;
        var songInPlaylist = service.GetById(songInPlaylistId);
    
        if (songInPlaylist == null)
            return false;
    
        if (songInPlaylist.Playlist == null)
            return false;
    
        result = songInPlaylist.Playlist.Id;
        return true;
    }
    

    这种方法的一个小问题是,您在尝试诊断问题时隐藏了可能有用的信息。也许添加Debug.WriteLine() 或其他形式的日志记录会很有用。关键是,您无法区分未找到播放列表 ID 的情况和找到但不包含播放列表的情况。

    否则,您可能会抛出一个包含更多信息的异常,例如:

    public long GetPlaylistId(long songInPlaylistId)
    {
        var songInPlaylist = service.GetById(songInPlaylistId);
    
        if (songInPlaylist == null)
            throw new InvalidOperationException("songInPlaylistId not found: " + songInPlaylistId);
    
        if (songInPlaylist.Playlist == null)
            throw new InvalidOperationException("Playlist for ID " + songInPlaylistId " has no playlist: ");
    
        return songInPlaylist.Playlist.Id;
    }
    

    在播放列表中找不到歌曲可能是有效的,但找到没有播放列表的歌曲是无效的,在这种情况下,您将在第一种情况下返回一个特殊值,并且第二种情况抛出异常,例如:

    public long GetPlaylistId(long songInPlaylistId)
    {
        var songInPlaylist = service.GetById(songInPlaylistId);
    
        if (songInPlaylist == null)
            return -1; // -1 means "playlist not found".
    
        if (songInPlaylist.Playlist == null)
            throw new InvalidOperationException("Playlist for ID " + songInPlaylistId " has no playlist: ");
    
        return songInPlaylist.Playlist.Id;
    }
    

    无论如何,我个人认为你的扩展方法只是在掩盖代码。

    【讨论】:

      【解决方案3】:
      try{
      if (obj != null)
      {
          return evaluator(obj);
      }
      }
      catch(Exception ex)
      {
      throw;
      }
      

      返回对象;

      除非被某些人抓住,否则不应抛出错误。在给定的情况下最好返回 null 并在您的调用代码中处理它:

      【讨论】:

      • 如果代码遇到任何错误,那么只有它会抛出错误,否则返回 obj(无论它持有什么值)
      • 在您的情况下,try/catch 根本没有做任何事情。代码在功能上是等效的,如果你删除它
      • 有什么相似之处!这段代码只会在 obj==null 的情况下返回 null 而被问到的代码会在 null 的情况下抛出异常!!
      • 请用感叹号停止它,这被认为是不礼貌的。在程序中尝试一下,自己看看。另请参阅此答案:stackoverflow.com/a/881489/451540
      • 对不起,如果觉得不礼貌:) 我会删除'!'
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-11
      • 2019-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多