如何在C#中使用Java样式的throws关键字?


89

在Java中,throws关键字允许方法声明自己不会处理异常,而是将其扔给调用方法。

C#中是否有类似的关键字/属性?

如果没有等效项,您如何才能实现相同(或相似)的效果?

Answers:


75

在Java中,您必须处理异常或将方法标记为可能使用throws关键字抛出该异常的方法。

C#没有此关键字或与之等效的关键字,如C#中那样,如果不处理异常,它将冒泡,直到被捕获或被捕获为止将终止程序。

如果要处理它,然后重新抛出,则可以执行以下操作:

try
{
  // code that throws an exception
}
catch(ArgumentNullException ex)
{
  // code that handles the exception
  throw;
}

1
“它会冒泡”,这是否意味着它等同于Java中所有具有throws子句的方法?
路易·瑞斯

1
@Louis RH-有点。这意味着异常(如果未处理)将通过每个调用函数上升到调用链中,直到被处理。
Oded

1
@Louis RH并不完全,这意味着您必须至少在Main中捕获Exception才能编译代码。因为C#不知道检查的异常,所以要由您来捕获它们,否则它们将仅在运行时进入并中断您的代码。
Johannes Wachter

1
@jwatcher:main方法也可以具有throw子句。
路易·瑞斯

4
@AshishKamble-嗯 意见事项。.NET中的异常处理有所不同。不要以为你知道什么更好。
Oded

105

操作员询问的是与Java throws子句等效C#,而不是throw关键字。在Java的方法签名中使用它来指示可以抛出已检查的异常。

在C#中,没有Java检查异常的直接等效项。C#没有等效的方法签名子句。

// Java - need to have throws clause if IOException not handled
public void readFile() throws java.io.IOException {
  ...not explicitly handling java.io.IOException...
}

转换为

// C# - no equivalent of throws clause exceptions are unchecked
public void ReadFile() 
{
  ...not explicitly handling System.IO.IOException...
}

30

是的,这是一个旧线程,但是我在搜索答案时经常发现旧线程,所以我想我会添加一些有用的东西。

如果您使用的是Visual Studio 2012,则有一个内置工具可用于允许等效的IDE级别“抛出”。

如果使用XML文档注释(如上所述),则可以使用<exception>标记指定方法或类引发的异常的类型,以及有关何时或为何引发该异常的信息。

例:

    /// <summary>This method throws an exception.</summary>
    /// <param name="myPath">A path to a directory that will be zipped.</param>
    /// <exception cref="IOException">This exception is thrown if the archive already exists</exception>
    public void FooThrowsAnException (string myPath)
    {
        // This will throw an IO exception
        ZipFile.CreateFromDirectory(myPath);
    }

4
OP,这是您的答案。我正在猜测,但是JAVA throws可能对运行时毫无意义,除了对开发人员有所帮助。同样,@ mvanella在这里指出的是C#完全相同的方法。我假设您已经知道此“ xml文档”具有更重要的用途。我知道这个线程很旧。
哈里·卢博瓦茨

实际上,除非在throws子句中明确指定或由throw命令抛出,否则Java不会冒出异常。这意味着,如果发生RunTimeException,并且未在发生该事件的相同方法中对其进行处理,则执行将在那里停止。
Pedro Lima

18

这是我在bytes.com上发现的类似问题的答案:

最简洁的答案是不。C#中没有检查过的异常。语言的设计师在这次采访中讨论了这个决定:

http://www.artima.com/intv/handcuffs.html

最接近的方法是使用XML文档中的标记,并将NDoc生成的文档与代码/程序集一起分发,以便其他人可以看到您抛出了哪些异常(这正是MS在MSDN文档中所做的)。但是,您不能依赖编译器来告诉您未处理的异常,就像您可能习惯于在Java中那样。


7

在查看完大多数答案之后,我想补充一些想法。

  1. 依靠XML文档注释并希望其他人依赖它是一个糟糕的选择。我遇到的大多数C#代码都没有完全和一致地用XML文档注释来记录方法。还有一个更大的问题,就是在C#中没有检查异常,如何记录方法抛出的所有异常,以使API用户知道如何单独处理所有异常?记住,您只知道在实现中使用throw关键字抛出自己的代码。您在方法实现中使用的API可能还会引发您不知道的异常,因为它们可能没有记录,并且您在实现中没有对其进行处理,因此面对您的调用者,它们会爆炸方法。换一种说法,

  2. Andreas将与Anders Hejlsberg的访谈联系在一起,回答了C#设计团队为何决定不检查异常的答案。对原始问题的最终回答隐藏在该采访中:

程序员通过在各处编写try try来保护代码,因此,如果发生异常,他们将正确退出,但是实际上他们对处理异常并不感兴趣。

换句话说,没有人会对特定API预期会发生什么样的异常感兴趣,因为您总是会无处不在地捕获所有异常。而且,如果您真的要关心特定的异常,则如何处理它们取决于您自己,而不是由像Java throws关键字这样的方法定义方法签名的人,它会强制对API用户进行特定的异常处理。

-

就个人而言,我在这里被撕裂了。我同意安德斯(Anders)的观点,即检查异常不会在不添加新的不同问题的情况下解决问题。就像XML文档注释一样,我很少看到C#代码将所有内容包装在try finally块中。在我看来,尽管这确实是您唯一的选择,但似乎是一种很好的做法。


3

实际上,没有在C#中检查异常可以被认为是一件好事或坏事。

我自己认为这是一个很好的解决方案,因为检查异常会为您带来以下问题:

  1. 技术异常泄漏到业务/域层,因为您不能在低层次上正确处理它们。
  2. 它们属于方法签名,在API设计中并不总是很好用。

因此,在大多数较大的应用程序中,选中的异常发生时,您经常会看到以下模式:

try {
    // Some Code
} catch(SomeException ex){
    throw new RuntimeException(ex);
}

从本质上讲,这意味着模拟C#/。NET处理所有异常的方式。


我无法想象被检查的异常将如何与lambda混合!
加布

@Gabe:我确定您可以提出一些概念,让您混合使用它们,但是就像我说的那样,Java中的检查异常在大多数情况下也不是一种好习惯,尤其是在更复杂的应用程序中。因此,它们不在C#中很好。
Johannes Wachter

3

您在问这个:

重新抛出异常

public void Method()
{
  try
  {
      int x = 0;
      int sum = 100/x;
  }
  catch(DivideByZeroException e)
  {
      throw;
  }
}

要么

static void Main() 
    {
        string s = null;

        if (s == null) 
        {
            throw new ArgumentNullException();
        }

        Console.Write("The string s is null"); // not executed
    }

3
+1以使用throw。通过使用它,堆栈跟踪不会丢失。
Giuseppe Accaputo

2

.Net CodeContract EnsuresOnThrow<>和Java throws描述符之间有一些短暂的相似之处,尽管两者之间也存在很大差异,但两者都可以向调用者发出信号,该异常可以从函数或方法中引发,

  • EnsuresOnThrow<>不仅说明了可以抛出哪些异常,还规定了可以保证抛出异常的条件-如果异常条件不容易识别,则在调用方法中这可能是相当繁琐的代码。Java throws提供了可能引发异常的指示(即IMO,.Net的重点是通过合同来证明throw,而在Java中,重点转移到了调用方,以确认发生异常的可能性)。
  • .NET CC不作区分之间经过VS未选中例外Java有,虽然CC手册中的2.2.2未提到对

“仅将异常后置条件用于调用者应期望作为API一部分的那些例外”

  • 在.Net中,调用者可以确定是否执行任何例外操作(例如,通过禁用合同)。在Java中,调用者必须执行某些操作,即使它throws在其接口上为相同的异常添加了。

代码合同手册在这里


0

如果c#方法的目的只是抛出一个异常(如js return type所示),我建议只返回该异常。参见下面的示例:

    public EntityNotFoundException GetEntityNotFoundException(Type entityType, object id)
    {
        return new EntityNotFoundException($"The object '{entityType.Name}' with given id '{id}' not found.");
    }

    public TEntity GetEntity<TEntity>(string id)
    {
        var entity = session.Get<TEntity>(id);
        if (entity == null)
            throw GetEntityNotFoundException(typeof(TEntity), id);
        return entity;
    }

0

对于那些想知道的人,您甚至不需要定义捕获的内容即可将其传递给下一个方法。如果您希望在一个主线程中进行所有错误处理,则可以捕获所有内容并将其传递,如下所示:

try {
    //your code here
}
catch {
    //this will throw any exceptions caught by this try/catch
    throw;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.