The C# using statement and suppressed exceptions compared to Java 7 try…with
Yesterday Java SE 7 was released. Last night I had the opportunity to play with some of it’s new features, including Java 7’s new “resource management” syntax. Of course, we have had this feature in C# for quite some time, but I was actually curious to see whether the Java people learned from the C# implementation and thus implemented it in a better way. Much to my surprise ;), they did. Let me elaborate……
The example
Take a look at the following C# class:
1: class MyResource : IDisposable
2: {
3:
4: public void DoWork()
5: {
6: Console.WriteLine("Working!!!!!!!!");
7: throw new ArgumentException("A");
8: }
9:
10:
11: public void Dispose()
12: {
13: throw new ArgumentException("B");
14: }
15: }
It’s a class that implements IDisposable (in Java 7 it’s called AutoClosable) so it can be used with C#’s using statement. Now imagine that we create a method “CallDoWork”, which wraps a call to DoWork in a using block. This “CallDoWork” method is called by an external caller. Can you guess what happens when the CallDoWork method is called? Will the caller get the “A” exception from the DoWork method or the “B” exception from the call to the Dispose method? Take a look at the following program:
1: class Program
2: {
3: static void Main(string[] args)
4: {
5:
6: try
7: {
8: CallDoWork();
9: }
10: catch (Exception ex)
11: {
12: Console.WriteLine(ex.Message);
13: }
14: Console.ReadKey();
15: }
16:
17:
18: private static void CallDoWork()
19: {
20: using (MyResource r = new MyResource())
21: {
22: r.DoWork();
23: }
24: }
25: }
So the question actually is, “What will be the output of this program? A or B?” Well here it is:
This means that that exception A has just disappeared, which could be a problem, since most exceptions should be logged somewhere. Let’s change the CallDoWorkMethod in a way that an exception from the DoWork method is logged and an exception from the Dispose method is also logged:
1: private static void CallDoWork()
2: {
3: try
4: {
5: using (MyResource r = new MyResource())
6: {
7: try
8: {
9: r.DoWork();
10: }
11: catch (Exception ex)
12: {
13: Console.WriteLine(ex.Message);
14: }
15: }
16: }
17: catch (Exception ex)
18: {
19: Console.WriteLine(ex.Message);
20: }
21: }
So how does the using block look now? Not quite as small right? Don’t worry, the alternative without a using statement is still worse:
1: MyResource r = null;
2: try
3: {
4: r = new MyResource();
5: r.DoWork();
6: }
7: catch (Exception ex)
8: {
9: Console.WriteLine(ex.Message);
10: }
11: finally
12: {
13: if (r != null)
14: {
15: try
16: {
17: r.Dispose();
18: }
19: catch (Exception ex)
20: {
21: Console.WriteLine(ex.Message);
22: }
23: }
24: }
Note that I haven’t even added code to wrap the exceptions and rethrow them. Also note that this isn’t a problem that’s purely theoretical. Take a look at the following code snippet:
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: try
6: {
7: CallService();
8: Console.WriteLine("Service call succeeded!");
9: }
10: catch (Exception ex)
11: {
12: Console.WriteLine("Unexpected error occured: " + ex.Message);
13: }
14: Console.ReadKey();
15: }
16:
17:
18: private static void CallService()
19: {
20: using (HelloServiceClient client = new HelloServiceClient())
21: {
22: client.DoWork();
23: }
24: }
25: }
The code in the main method calls CallService defined on line 18. This methods wraps a call to a WCF Service in a using block. This WCF Service uses a wsHttpBinding. Now take a look at the output if the service is down:
This is NOT the error message from the call to the proxy, but it’s an error message from an exception that get’s thrown because the Dispose method was called while the channel was in the “Faulted” state. Now I know that in WCF you should check before you dispose and that you shouldn’t use proxies in a using statement, but I wanted you to show that there are framework Dispose methods that actually throw exceptions. Next to that, you might never know for certain whether a third party component throws an exception in a Dispose method. In a real life application you’ll probably don’t sprinkle exception handling code like the above throughout your application, you’ll probable create some generic solution or handle exceptions in a certain layer of your application. I just want to make you aware that a using statement can suppress an exception and that this exception might never reach your exception handling solution or your exception handling layer.
The Java way and the new Try…With syntax
As I previously stated, the guys at Oracle have learned from the using statement in C# and implemented a nice solution:
1: public class TryWithRes
2: {
3: public static void main(String[] args)
4: {
5: try(NewResource res = new NewResource("Res1 closing"))
6: {
7: res.doWork();
8: }
9: catch(Exception e)
10: {
11: System.out.println("Exception thrown by doWork()" + e);
12: if(e.getSuppressed() != null)
13: {
14: for(Throwable t : e.getSuppressed())
15: {
16: System.out.println("Exception thrown by close(): " + t);
17: }
18: }
19: }
20: }
21: }
I have formatted the code in a C# way so all you C# guys can follow along ;). Instead of the “using” keyword, Java 7 uses the try keyword on line 5. I’ve seen some people on the internet complaining about the “try” on line 5 instead of a new keyword, but because of the try keyword, the catch on line 9 feels very natural to me. Java 7 also shows different behavior, the exception that get’s caught on line 9 isn’t the exception that get’s thrown by the close() method of NewResource, like it would be the case when you surrounded a using block in C# with try..catch, but it’s the exception from doWork(). Next to that, the Exception class was expanded with a new “getSuppressed()” method (shown on lines 12 and 14), which you can use to get the exception from the close method. Technically, I didn’t need a foreach loop on line 14, this would be needed if I nested multiple try…with statements, but I just wanted to show that getSuppressed() returns a collection.
You don’t have to provide a catch when using the new try…with syntax, so now you can create a generic exception handling solution or layer and you can log all the exceptions thanks to the new getSuppressed() method, without losing any of them and while still using automatic resource management. If you want to know more about the new Java 7 try…with syntax, you can find an excellent article on the Oracle website here.
Conclusion
I have to say, I really like the new try…with syntax and also how suppressed exceptions are dealt with. The big difference between “using” and “try…with” is obviously that “using” in C# generates a “try…finally” in IL, while “try…with” in Java generates a “try…catch…finally” in bytecode. The “catch” stores the primary exception and when the “finally” executes and an exception is thrown, this exception is added to the primary stored exception using the new “addSuppressed()” method. After this, the stored primary exception which holds the suppressed exception, is thrown from the finally block.
I think this a perfect example of how .Net and Java drive each other to get better and I can only congratulate Oracle and the Java community on a great Java 7 SE release!