c#异常处理,实例.你会怎么做?

Too*_*eey 13 c# exception-handling

我正试图在处理异常方面做得更好,但是当我尽力抓住它们时,我觉得我的代码变得非常丑陋,难以理解和混乱.我很想看看其他人如何通过提供实际例子和比较解决方案来解决这个问题.

我的示例方法从URL下载数据并尝试将其序列化为给定类型,然后返回填充了数据的实例.

首先,没有任何异常处理:

    private static T LoadAndSerialize<T>(string url)
    {            
        var uri = new Uri(url);
        var request = WebRequest.Create(uri);
        var response = request.GetResponse();
        var stream = response.GetResponseStream();

        var result = Activator.CreateInstance<T>();
        var serializer = new DataContractJsonSerializer(result.GetType());
        return (T)serializer.ReadObject(stream);            
    }
Run Code Online (Sandbox Code Playgroud)

我觉得这个方法很可读.我知道这个方法中有一些不必要的步骤(比如WebRequest.Create()可以接受一个字符串,我可以链接方法而不给它们变量)但是我会这样做,以便更好地与具有异常的版本进行比较 - 处理.

这是处理可能出错的一切的第一次尝试:

    private static T LoadAndSerialize<T>(string url)
    {
        Uri uri;
        WebRequest request;
        WebResponse response;
        Stream stream;
        T instance;
        DataContractJsonSerializer serializer;

        try
        {
            uri = new Uri(url);
        }
        catch (Exception e)
        {
            throw new Exception("LoadAndSerialize : Parameter 'url' is malformed or missing.", e);
        }

        try
        {
            request = WebRequest.Create(uri);
        }
        catch (Exception e)
        {
            throw new Exception("LoadAndSerialize : Unable to create WebRequest.", e);
        }

        try
        {
            response = request.GetResponse();
        }
        catch (Exception e)
        {
            throw new Exception(string.Format("LoadAndSerialize : Error while getting response from host '{0}'.", uri.Host), e);
        }

        if (response == null) throw new Exception(string.Format("LoadAndSerialize : No response from host '{0}'.", uri.Host));

        try
        {
            stream = response.GetResponseStream();
        }
        catch (Exception e)
        {
            throw new Exception("LoadAndSerialize : Unable to get stream from response.", e);
        }

        if (stream == null) throw new Exception("LoadAndSerialize : Unable to get a stream from response.");

        try
        {
            instance = Activator.CreateInstance<T>();
        }
        catch (Exception e)
        {
            throw new Exception(string.Format("LoadAndSerialize : Unable to create and instance of '{0}' (no parameterless constructor?).", typeof(T).Name), e);
        }

        try
        {
            serializer = new DataContractJsonSerializer(instance.GetType());
        }
        catch (Exception e)
        {

            throw new Exception(string.Format("LoadAndSerialize : Unable to create serializer for '{0}' (databinding issues?).", typeof(T).Name), e);
        }


        try
        {
            instance = (T)serializer.ReadObject(stream);
        }
        catch (Exception e)
        {
            throw new Exception(string.Format("LoadAndSerialize : Unable to serialize stream into '{0}'.", typeof(T).Name), e);                   
        }

        return instance;
    }
Run Code Online (Sandbox Code Playgroud)

这里的问题是,虽然可能出错的一切都会被抓住并且给出一个有意义的例外,但这是一个非常重要的混乱.

那么,如果我把捕获链接起来怎么办呢.我的下一次尝试是这样的:

    private static T LoadAndSerialize<T>(string url)
    {
        try
        {
            var uri = new Uri(url);
            var request = WebRequest.Create(uri);
            var response = request.GetResponse();
            var stream = response.GetResponseStream();
            var serializer = new DataContractJsonSerializer(typeof(T));
            return (T)serializer.ReadObject(stream);
        }
        catch (ArgumentNullException e)
        {
            throw new Exception("LoadAndSerialize : Parameter 'url' cannot be null.", e);
        }             
        catch (UriFormatException e)
        {
            throw new Exception("LoadAndSerialize : Parameter 'url' is malformed.", e);
        }
        catch (NotSupportedException e)
        {
            throw new Exception("LoadAndSerialize : Unable to create WebRequest or get response stream, operation not supported.", e);
        }
        catch (System.Security.SecurityException e)
        {
            throw new Exception("LoadAndSerialize : Unable to create WebRequest, operation was prohibited.", e);
        }
        catch (NotImplementedException e)
        {
            throw new Exception("LoadAndSerialize : Unable to get response from WebRequest, method not implemented?!.", e);
        }
        catch(NullReferenceException e)
        {
            throw new Exception("LoadAndSerialize : Response or stream was empty.", e);
        }
    }
Run Code Online (Sandbox Code Playgroud)

虽然它在眼睛上肯定更容易,但我在这里大量倾向于智能感知,以提供可能从方法或类中抛出的所有异常.我不相信这个文档是100%准确的,如果某些方法来自.net框架之外的程序集,我会更加怀疑.例如,DataContractJsonSerializer在intellisense上没有显示异常.这是否意味着构造函数永远不会失败?我能确定吗?

与此相关的其他问题是,某些方法会抛出相同的异常,这会使错误更难描述(此或此或出错),因此对用户/调试器没那么有用.

第三种选择是忽略除允许我采取重试连接之类的操作之外的所有异常.如果url为null,则url为null,捕获的唯一好处是更详细的错误消息.

我很想看到你的想法和/或实施!

Ode*_*ded 20

规则之一的异常处理 - 不捕获您不知道如何处理的异常.

仅仅为了提供好的错误消息而捕获异常是值得怀疑的.异常类型和消息已经包含了开发人员的足够信息- 您提供的消息不会添加任何值.

DataContractJsonSerializer在intellisense上没有显示异常.这是否意味着构造函数永远不会失败?我能确定吗?

不,你不能确定.通常,C#和.NET不像Java那样必须声明可能引发的异常.

第三种选择是忽略除允许我采取重试连接之类的操作之外的所有异常.

这确实是最好的选择.

您还可以在应用程序的顶部添加一个通用异常处理程序,它将捕获所有未处理的异常并记录它们.


Eri*_*ert 13

首先,阅读我关于异常处理的文章:

http://ericlippert.com/2008/09/10/vexing-exceptions/

我的建议是:您必须处理代码可能抛出的"烦恼异常"和"外部异常".Vexing例外是"非例外"例外,所以你必须处理它们.由于您无法控制的考虑因素,可能会发生外部异常,因此您必须处理它们.

不能处理致命和愚蠢的例外.你不需要处理的骨头异常,因为你永远不会做任何导致它们被抛出的事情.如果他们被抛出然后你有一个错误,解决方案是修复错误.不要处理异常; 那隐藏了这个bug.并且你无法有意义地处理致命异常,因为它们是致命的.这个过程即将结束.您可能会考虑记录致命异常,但请记住,日志记录子系统可能是首先触发致命异常的事情.

总之:只处理那些异常也可能会发生,你知道如何处理.如果您不知道如何处理它,请将它留给您的来电者; 来电者可能比你知道得更好.

在您的特定情况下:不要处理此方法中的任何异常.让调用者处理异常.如果调用者通过了一个无法解析的URL,请将其崩溃.如果坏网址是一个错误,那么调用者就有一个错误需要修复,而你正在通过引起他们的注意来帮助它们.如果坏网址不是错误 - 例如,因为用户的互联网连接搞砸了 - 那么调用者需要通过查询真实异常来找出获取失败原因.来电者可能知道如何恢复,所以请帮助他们.