抛出异常的最佳方法

时间:2014-04-10 07:20:06

标签: c# exception null monads

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

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'"));
}

Monadic扩展方法:

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;
}

3 个答案:

答案 0 :(得分:3)

如果尝试获取没有播放列表的播放列表是有效的,那么您不应该抛出异常,而应该返回一个特殊值,这意味着&#34;未找到&#34;相反(例如,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的情况,以及找不到播放列表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;
}

无论如何,我个人认为你的扩展方法只是模糊了代码。

答案 1 :(得分:0)

try{
if (obj != null)
{
    return evaluator(obj);
}
}
catch(Exception ex)
{
throw;
}

return obj;

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

答案 2 :(得分:0)

如果我班上有多个这样模棱两可的方法,会发生什么?为任何方法制定不同的规则是非常困难的。最后你会感到困惑。 您对此解决方案有何看法?

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
}

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