捕获预期的例外情况

时间:2010-01-02 01:13:26

标签: c# exception

我有一些看起来像这样的代码因为我检查某些数据是图像的唯一可靠方式是实际尝试加载它像图像一样。

    static void DownloadCompleted(HttpConnection conn) {
        Image img;
        HtmlDocument doc;

        try {
            img = Image.FromStream(conn.Stream);
        } catch {
            try {
                doc = new HtmlDocument();
                doc.Load(conn.Stream);
            } catch { return; }
            ProcessDocument(doc);
            return;
        }
        ProcessImage(img);
        return;
    }

看起来很可怕!

处理这些情况的好方法是什么?您基本上被迫使用if语句之类的异常?

6 个答案:

答案 0 :(得分:6)

您的逻辑结构是

if( /* Loading Image Fails */ )
    /* Try Loading HTML */

所以我会尝试以这种方式读取代码。引入辅助方法可能是最干净的(尽管通常令人讨厌):

bool LoadImage()
{
    Image img;
    try 
    {
        img = Image.FromStream(conn.Stream);
    } 
    catch( NotAnImageException /* or whatever it is */ )
    {
        return false;
    }

    ProcessImage(img);
    return true;
}


bool LoadDocument()
{
   // etc
}

所以你可以写

if( !LoadImage() )
   LoadDocument();

或将其扩展为:

if( !LoadImage() && !LoadDocument() )
{
     /* Complain */
}

答案 1 :(得分:1)

我认为空的捕获块,你编码它们的方式,总的来说是一个非常糟糕的主意。你在那里吞下一个例外。没有人会知道你编码它的方式有些不对。

我的政策是,如果我无法处理并从异常中恢复,我应该简单地允许它将调用堆栈冒泡到可以处理的地方。这可能仅仅意味着记录错误,但它比隐藏错误更好。

如果未经检查的异常是.NET中的规则,我建议您在单个方法中必须使用一个try / catch块。我同意 - 方法中的多个try / catch块是丑陋和混乱的。

我不确定您的代码尝试做什么。看起来你正在使用异常作为逻辑:“如果没有抛出异常,则处理并返回一个图像;如果抛出异常,则处理并返回一个HTML文档。”我认为这是一个糟糕的设计。我将它重构为两个方法,一个用于图像和HTML,而不是使用异常而不是“if”测试。

答案 2 :(得分:1)

与duffymo的答案相结合,如果你正在处理

  • 文件输入/输出,使用IOException
  • 网络套接字,使用SocketException
  • XML,使用XmlException

这样可以使异常更加整洁,因为你不允许它冒泡到通用的catch-all异常。它可能很慢,如果你使用泛型catch-all异常,因为堆栈跟踪将冒泡并占用内存作为对Exception属性的引用,因此InnerException会嵌套为结果。

无论哪种方式,制作代码以显示异常并在必要时记录它或重新抛出它以捕获全部异常...永远不要假设代码不会因为内存充足,磁盘充足而失败等等,因为那是在脚下射击自己。

如果要构建一组唯一的类和API,则要设计自己的Exception类,请继承ApplicationException类。

修改 我更了解OP正在做什么...我建议以下,更清晰的实现,注意我如何检查流以查看是否有HTML标签或DOC标签作为XHTML的一部分......

static void DownloadCompleted(HttpConnection conn) {
        Image img;
        HtmlDocument doc;
        bool bText = false;
        using (System.IO.BinaryReader br = new BinaryReader(conn.Stream)){
            char[] chPeek = br.ReadChars(30);
            string s = chPeek.ToString().Replace(" ", "");
            if (s.IndexOf("<DOC") > 0 || s.IndexOf("<HTML") > 0){
                // We have a pure Text!
                bText = true;
            }
        }
        if (bText){
            doc = new HtmlDocument();
            doc.Load(conn.Stream);
        }else{
            img = Image.FromStream(conn.Stream);
        }
}

如果您愿意,请根据指示html标签位置的conn.Stream的距离来增加长度...

希望这有帮助, 最好的祝福, 汤姆。

答案 3 :(得分:1)

我喜欢Eric的例子,但我发现实现很难看:在if中工作并且有一个方法可以返回一个布尔看起来非常难看。

我的选择是返回图像的方法,以及类似的方法

Image LoadImage(HttpConnection conn)
{
    try 
    {
        return Image.FromStream(conn.Stream);
    } 
    catch(NotAnImageException)
    {
        return null;
    }
}

并以原始方法完成工作:

static void DownloadCompleted(HttpConnection conn)
{
    Image img = LoadImage(conn);

    if(img != null)
    {
         ProcessImage(img);
         return;
    }

    HtmlDocument doc = LoadDocument(conn);

    if(doc != null)
    {
         ProcessDocument(doc)
         return;
    }

    return;
}

答案 4 :(得分:1)

无需尝试猜测 HTTP下载的内容类型,这就是您在此处捕获异常的原因。 HTTP协议定义了Content-Type标头字段,为您提供下载内容的MIME type

此外,不可搜索的流只能读取一次。要多次阅读流的内容,您必须copy it to一个MemoryStream并在每次使用Seek(0, SeekOrigin.Begin)的阅读会话之前重置它。

答案 5 :(得分:0)

回答你的确切问题:你根本不需要一个catch块,try / finally块在没有catch块的情况下工作正常。

try
{
    int i = 0;
}
finally {}