C# try-catch-else
从Python到C#的异常处理困扰着我的一件事是,在C#中似乎没有任何指定else子句的方法。例如,在Python中,我可以编写这样的内容(请注意,这只是一个示例。我不是在问什么是读取文件的最佳方法):
1 2 3 4 5 6 7 8 9 10 11 12 13 | try { reader = new StreamReader(path); } catch (Exception) { // Uh oh something went wrong with opening the file for reading } else { string line = reader.ReadLine(); char character = line[30]; } |
根据我在大多数C#代码中看到的内容,人们只会编写以下内容:
1 2 3 4 5 6 7 8 9 10 | try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch (Exception) { // Uh oh something went wrong, but where? } |
此问题是我不想超出范围异常,原因是文件的第一行可能包含不超过30个字符。我只想捕获与读取文件流有关的异常。我可以在C#中使用任何类似的构造来实现相同的功能吗?
捕获特定类别的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch (IOException ex) { // Uh oh something went wrong with I/O } catch (Exception ex) { // Uh oh something else went wrong throw; // unless you're very sure what you're doing here. } |
第二个捕获当然是可选的。而且由于您不知道发生了什么,因此吞下这个最一般的异常非常危险。
您可以这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | bool success = false; try { reader = new StreamReader(path); success = true; } catch(Exception) { // Uh oh something went wrong with opening the file for reading } finally { if(success) { string line = reader.ReadLine(); char character = line[30]; } } |
捕获更具体的异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch(FileNotFoundException e) { // thrown by StreamReader constructor } catch(DirectoryNotFoundException e) { // thrown by StreamReader constructor } catch(IOException e) { // some other fatal IO error occured } |
通常,进一步处理尽可能特殊的异常,并避免处理基本的
您可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 | try { reader = new StreamReader(path); } catch (Exception) { // Uh oh something went wrong with opening the file for reading } string line = reader.ReadLine(); char character = line[30]; |
但是,当然,您必须将
您也可以嵌套try语句
.NET中对异常的用法有所不同;它们仅适用于特殊情况。
实际上,除非您知道异常的含义并且可以对此采取实际措施,否则您不应捕获异常。
Is there any similar construct I can use in C#
to acheive the same thing?
不。
用" if "语句package索引访问器,这是在性能和??可读性方面最好的解决方案。
1 2 3 | if (line.length > 30) { char character = line [30]; } |
习惯上来说,您将使用
1 2 3 4 5 6 7 8 9 10 | try { using (reader = new StreamReader(path)) { DoSomethingWith(reader); } } catch(IOException ex) { // Log ex here } |
最好也避免捕获所有可能的异常-就像那些告诉您运行时即将到期的异常一样。
您可以有多个catch子句,每个子句都特定于您希望捕获的异常类型。因此,如果只想捕获IOExceptions,则可以将catch子句更改为:
1 2 3 4 5 6 7 8 9 | try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch (IOException) { } |
除IOException外,其他任何东西都将在调用堆栈中传播。如果您还想处理其他异常,则可以添加多个异常子句,但是必须确保以最特定于大多数通用顺序的方式添加它们。例如:
1 2 3 4 5 6 7 8 9 10 11 12 | try { reader = new StreamReader(path); string line = reader.ReadLine(); char character = line[30]; } catch (IOException) { } catch (Exception) { } |
在C#中可能没有对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | using System; public class Test { public static void Main() { Example("ksEE5A.exe"); } public static char Example(string path) { var reader = default(System.IO.StreamReader); var line = default(string); var character = default(char); TryElse( delegate { Console.WriteLine("Trying to open StreamReader ..."); reader = new System.IO.StreamReader(path); }, delegate { Console.WriteLine("Success!"); line = reader.ReadLine(); character = line[30]; }, null, new Case(typeof(NullReferenceException), error => { Console.WriteLine("Something was null and should not have been."); Console.WriteLine("The line variable could not cause this error."); }), new Case(typeof(System.IO.FileNotFoundException), error => { Console.WriteLine("File could not be found:"); Console.WriteLine(path); }), new Case(typeof(Exception), error => { Console.WriteLine("There was an error:"); Console.WriteLine(error); })); return character; } public static void TryElse(Action pyTry, Action pyElse, Action pyFinally, params Case[] pyExcept) { if (pyElse != null && pyExcept.Length < 1) { throw new ArgumentException(@"there must be exception handlers if else is specified", nameof(pyExcept)); } var doElse = false; var savedError = default(Exception); try { try { pyTry(); doElse = true; } catch (Exception error) { savedError = error; foreach (var handler in pyExcept) { if (handler.IsMatch(error)) { handler.Process(error); savedError = null; break; } } } if (doElse) { pyElse(); } } catch (Exception error) { savedError = error; } pyFinally?.Invoke(); if (savedError != null) { throw savedError; } } } public class Case { private Type ExceptionType { get; } public Action<Exception> Process { get; } private Func<Exception, bool> When { get; } public Case(Type exceptionType, Action<Exception> handler, Func<Exception, bool> when = null) { if (!typeof(Exception).IsAssignableFrom(exceptionType)) { throw new ArgumentException(@"exceptionType must be a type of exception", nameof(exceptionType)); } this.ExceptionType = exceptionType; this.Process = handler; this.When = when; } public bool IsMatch(Exception error) { return this.ExceptionType.IsInstanceOfType(error) && (this.When?.Invoke(error) ?? true); } } |
听起来像您只想在第一件事成功的情况下才做第二件事。也许捕获不同类别的异常是不合适的,例如,如果两个语句都可以抛出相同类别的异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | try { reader1 = new StreamReader(path1); // if we got this far, path 1 succeded, so try path2 try { reader2 = new StreamReader(path2); } catch (OIException ex) { // Uh oh something went wrong with opening the file2 for reading // Nevertheless, have a look at file1. Its fine! } } catch (OIException ex) { // Uh oh something went wrong with opening the file1 for reading. // So I didn't even try to open file2 } |
看到其他建议的解决方案后,这是我的方法:
1 2 3 4 5 6 7 8 9 10 11 |
当然,只有在您对通过打开文件流(在此处为示例)引发的任何异常感兴趣的情况下,这样做才有意义,并且与应用程序中的所有其他异常分开。在应用程序的更高级别,然后您可以按照自己的意愿处理
由于未检查的异常,您永远不能百分百确定仅从整个代码块中捕获
我已经自由地对您的代码进行了一些改动,以说明一些要点。
通过研究
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // May throw FileNotFoundException, DirectoryNotFoundException, // IOException and more. try { using (StreamReader streamReader = new StreamReader(path)) { try { String line; // May throw IOException. while ((line = streamReader.ReadLine()) != null) { // May throw IndexOutOfRangeException. Char c = line[30]; Console.WriteLine(c); } } catch (IOException ex) { Console.WriteLine("Error reading file:" + ex.Message); } } } catch (FileNotFoundException ex) { Console.WriteLine("File does not exists:" + ex.Message); } catch (DirectoryNotFoundException ex) { Console.WriteLine("Invalid path:" + ex.Message); } catch (IOException ex) { Console.WriteLine("Error reading file:" + ex.Message); } |
如果碰巧处于循环中,则可以将catch语句放入catch块中。这将导致跳过该块的其余代码。
如果您不在循环中,则无需在此级别捕获异常。让它沿调用堆栈向上传播到知道如何处理的catch块。您可以通过在当前级别消除整个try / catch框架来做到这一点。
我也喜欢在Python中使用try / except / else,也许有一天它们会被添加到C#中(就像有多个返回值一样)。但是,如果您对异常的看法有所不同,则其他块并不是严格必需的。