从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#中使用任何类似的构造来实现同样的目的吗?
答案 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#中(就像有多个返回值一样)。但是,如果您对异常的看法有所不同,那么严格来说就不需要块了。