代码之家  ›  专栏  ›  技术社区  ›  mpen

捕获预期的异常

  •  2
  • mpen  · 技术社区  · 15 年前

    我有这样的代码,因为 可靠的 我检查一些数据是否是图像的方法是尝试像图像一样加载它。

        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 回复  |  直到 15 年前
        1
  •  6
  •   Eric    15 年前

    你的逻辑结构是

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

    所以我会尝试让代码以这种方式读取。引入helper方法可能是最干净的(尽管无疑是令人讨厌的冗长):

    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 */
    }
    
        2
  •  1
  •   duffymo    15 年前

    我认为,空的catch块,即您对其进行编码的方式,通常是一个非常糟糕的主意。你在那里接受了一个例外。没有人会知道你的编码方式有问题。

    我的策略是,如果我不能处理并从异常中恢复,我应该简单地允许它在调用堆栈中冒泡到一个可以处理它的地方。这可能只意味着记录错误,但总比隐藏错误好。

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

    我不知道你的代码在做什么。您似乎在使用异常作为逻辑:“如果未引发异常,则处理并返回图像;如果引发异常,则处理并返回HTML文档。”我认为这是一个糟糕的设计。我将它重构为两种方法,一种用于图像和HTML,不使用异常而使用“if”测试。

        3
  •  1
  •   Tordek    15 年前

    我喜欢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
  •   t0mm13b    15 年前

    结合达菲莫的回答,如果你正在处理

    • 文件输入/输出,使用IOException
    • 网络套接字,使用socketexception
    • XML,使用XML异常

    这将使捕获异常更加整洁,因为您不允许它冒泡到一般的“全部捕获”异常。这可能很慢,如果使用通用的catch all异常,因为堆栈跟踪将冒泡并占用内存作为对异常属性的引用,则会导致innerException嵌套。

    不管是哪种方式,编写代码来显示异常,并在必要时记录它,或者让它被“捕获所有异常”捕获……永远不要假设代码不会失败,因为有足够的内存、足够的磁盘等等,这会让你自食其果。

    如果要设计自己的异常类(如果要构建一组唯一的类和API),请从applicationException类继承。

    编辑: 我更好地理解OP正在做什么……我建议下面更清晰的实现,注意我如何检查流以查看XHTML中是否有HTML标记或DOC标记…

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

    如果您愿意,请增加长度,具体取决于 conn.Stream 指示HTML标记的位置…

    希望这有帮助, 最好的问候, 汤姆。

        5
  •  1
  •   Wim Coenen    15 年前

    没有必要尝试 猜测 HTTP下载的内容类型,这就是您在这里捕获异常的原因。HTTP协议定义了 Content-Type 标题字段,它为您提供 MIME type 下载的内容。

    此外,不可查找的流只能读取一次。要多次读取流的内容,必须 copy it to MemoryStream 并在每次读取会话之前用 Seek(0, SeekOrigin.Begin) .

        6
  •  0
  •   Egor Pavlikhin    15 年前

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

    try
    {
        int i = 0;
    }
    finally {}