C#try-catch-else

时间:2009-07-24 12:47:23

标签: c# exception-handling

从Python到C#的异常处理让我感到困惑的一件事是,在C#中似乎没有任何指定else子句的方法。例如,在Python中我可以写这样的东西(注意,这只是一个例子。我不是问什么是读取文件的最佳方法):

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#代码中看到的,人们只会写下以下内容:

try
{
    reader = new StreamReader(path);
    string line = reader.ReadLine();
    char character = line[30];
}
catch (Exception)
{
    // Uh oh something went wrong, but where?
}

这样做的问题在于,我不希望因为文件中的第一行不能包含超过30个字符而超出范围异常。我只想捕获与读取文件流有关的异常。我可以在C#中使用任何类似的构造来实现同样的目的吗?

14 个答案:

答案 0 :(得分:46)

抓住特定类别的例外

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 :(得分:12)

你可以这样写:

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

答案 2 :(得分:8)

你可以这样做:

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

但是,当然,您必须将reader设置为正确的状态,或将return设置为方法。

答案 3 :(得分:6)

抓住更具体的例外情况。

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
}

此外,通常,尽可能处理最具体的异常并避免处理基础System.Exception

答案 4 :(得分:4)

.NET中的异常使用方式不同;它们仅用于特殊条件。

事实上,除非你知道它的含义,否则你不应该捕获异常,并且实际上可以做一些关于它的事情。

答案 5 :(得分:2)

您可以拥有多个catch子句,每个子句都特定于您希望捕获的异常类型。因此,如果您只想捕获IOExceptions,那么您可以将catch子句更改为:

try
{
    reader = new StreamReader(path);
    string line = reader.ReadLine();
    char character = line[30];
}
catch (IOException)
{    
}

除了IOException之外的任何内容都会向上传播调用堆栈。如果您还想处理其他异常,则可以添加多个异常子句,但必须确保将它们添加到大多数特定于最常规的顺序中。例如:

try
{
    reader = new StreamReader(path);
    string line = reader.ReadLine();
    char character = line[30];
}
catch (IOException)
{    
}
catch (Exception)
{
}

答案 6 :(得分:2)

更惯用的是,您将使用using语句将文件打开操作与其包含的数据所做的工作分开(并在退出时包括自动清理)

try {
  using (reader = new StreamReader(path))
  {
    DoSomethingWith(reader);
  }
} 
catch(IOException ex)
{
  // Log ex here
}

最好避免捕获每个可能的异常 - 比如那些告诉你运行时即将到期的异常。

答案 7 :(得分:2)

您也可以嵌套您的try语句

答案 8 :(得分:1)

  

我可以在C#中使用任何类似的构造吗?   能够实现同样的目标吗?

没有。

使用“if”语句包装索引访问器,这是性能和可读性方面的最佳解决方案。

if (line.length > 30) {
   char character = line [30];
} 

答案 9 :(得分:1)

在看到其他建议的解决方案之后,这是我的方法:

try {
    reader = new StreamReader(path);
}
catch(Exception ex) {
    // Uh oh something went wrong with opening the file stream
    MyOpeningFileStreamException newEx = new MyOpeningFileStreamException();
    newEx.InnerException = ex;
    throw(newEx);
}
    string line = reader.ReadLine();
    char character = line[30];

当然,只有当您对通过打开文件流(例如此处)分开所引发的任何异常感兴趣时,这样做才有意义。应用程序。在应用程序的某个更高级别,您可以根据需要处理MyOpeningFileStreamException

由于未经检查的异常,您永远不能100%确定仅从整个代码块中捕获IOException就足够了 - StreamReader也可以决定抛出其他类型的异常,现在或将来。

答案 10 :(得分:0)

我已经冒昧地改变了你的代码,以展示一些重要的观点。

using构造用于打开文件。如果抛出异常,即使您没有捕获异常,也必须记住关闭该文件。这可以使用try { } catch () { } finally { }构造来完成,但using指令对此更好。它保证当using块的范围结束时,内部创建的变量将被处理掉。对于文件,这意味着它将被关闭。

通过研究StreamReader构造函数和ReadLine方法的文档,您可以看到可能会抛出哪些异常。然后你可以抓住你认为合适的那些。请注意,记录的例外列表并不总是完整的。

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

答案 11 :(得分:0)

听起来你只想在第一件事成功的时候做第二件事。并且可能捕获不同类别的异常是不合适的,例如,如果两个语句都抛出同一类异常。

from rpy2.robjects import aov
from rpy2.robjects import Formula

formula = Formula('human_den ~ region + years')
env = formula.environment
env['human_den'] = df1['human_den']
env['region'] = df1['region']
env['years'] = df1['years']

huanova = aov(formula = formula)

答案 12 :(得分:0)

在C#中可能没有对try { ... } catch { ... } else { ... }的任何本机支持,但是如果您愿意承担使用替代方法的开销,那么下面显示的示例可能会很有吸引力:

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

答案 13 :(得分:0)

如果碰巧处于循环中,则可以将catch语句放入catch块中。这将导致该块的其余代码被跳过。

如果您不在循环中,则无需在此级别捕获异常。让它沿调用栈向上传播到一个知道如何处理的catch块。通过消除当前级别的整个try / catch框架,可以做到这一点。

我也喜欢在Python中使用try / except / else,也许有一天它们会被添加到C#中(就像有多个返回值一样)。但是,如果您对异常的看法有所不同,那么严格来说就不需要块了。