7

Obviously a lot of applications will need to work with files and display errors to users. However memebers of System.IO.File class throw a lot of exceptions. These are just for ReadAllText:

  • ArgumentException
  • ArgumentNullException
  • PathTooLongException
  • DirectoryNotFoundException
  • IOException
  • UnauthorizedAccessException
  • FileNotFoundException
  • NotSupportedException
  • SecurityException

So how to catch them all and display them to user while not swallowing other exceptions?

Obviously with perfect coding you can eliminate these 2:

  • ArgumentException
  • ArgumentNullException

If you write a (possibly painful) check you can eliminate PathTooLongException. But why would you duplicate code for check that Microsoft has written?

But other exceptions can still happen even if you did all the checks:

  • DirectoryNotFoundException
  • IOException
  • UnauthorizedAccessException
  • FileNotFoundException
  • NotSupportedException
  • SecurityException

Files and folders can get deleted by the time you open the file, security permissions can change etc.

I don't see what you can do in these scenarios except display message to user. Are you going to find directory that OS can't find? Fix the permissions? Inject code into OS to make unsupported operation supported? LOL All I see possible is to display an error message.

So if I have to catch all of these exceptions every time I open a file to read text my code would have to be long and repetitive, unless I swallow exceptions by catching Exception.

Would it be good practice to create a FileException and just catch all exceptions that can come up when actually working with files? What I had in mind is this:

public class FileException : Exception
{
    public FileException( Exception e )
        : base( e.Message, e.InnerException )
    {
    }
}

public static class FileNoBS
{
    public static string ReadAllText2( string path )
    {
        try
        {
            return File.ReadAllText( path );
        }
        catch ( ArgumentNullException e )
        {
            throw new FileException( e );
        }
        catch ( ArgumentException e )
        {
            throw new FileException( e );
        }
        catch ( PathTooLongException e )
        {
            throw new FileException( e );
        }
        catch ( DirectoryNotFoundException e )
        {
            throw new FileException( e );
        }
        catch ( FileNotFoundException e )
        {
            throw new FileException( e );
        }
        catch ( IOException e )
        {
            throw new FileException( e );
        }
        catch ( UnauthorizedAccessException e )
        {
            throw new FileException( e );
        }
        catch ( NotSupportedException e )
        {
            throw new FileException( e );
        }
        catch ( SecurityException e )
        {
            throw new FileException( e );
        }
    }    
}

Then when catching exceptions I would just have to write this:

        try
        {
            string text = FileNoBS.ReadAllText2( path );
        }
        catch ( FileException e )
        {
            // display error to user
        }

I don't really understand why Microsoft has not grouped all those exceptions togather in some way. Am I missing something or is this good practice?

Rui Jarimba
  • 11,166
  • 11
  • 56
  • 86
Marko
  • 161
  • 2
  • 9
  • 1
    But what is the practical result of this? You don't do anything specific for all these exceptions. If you really have to do something different for each one you end up testing every one in the same way. Catch just the generic Exception and print its message – Steve May 27 '13 at 10:29
  • Purpose is not to swallow exceptions I can't do anything about. I can do something specific - show user that error has occured. – Marko May 27 '13 at 10:32
  • @Steve then you'll miss StackOverflow or OutOfMemory exceptions. Current code focuses on File exceptions only. – Artemix May 27 '13 at 10:33
  • 1
    @Marko And how could Microsoft know what are your intentions? – Steve May 27 '13 at 10:35

2 Answers2

4

The exceptions that you listed are in two different categories - these indicating a coding error, and these indicating a run-time issue. You are absolutely right that the exceptions in the first category are preventable: you can write code in such a way that they never happen. For example, if your code null-checks the path, you are in no danger of ever getting ArgumentNullException in the call of ReadAllText. Let's analyse the remaining exceptions one by one:

  • IOException, DirectoryNotFoundException, FileNotFoundException - all three will be caught if you catch IOException
  • UnauthorizedAccessException - should be caught separately
  • NotSupportedException - can be prevented by validating the path before making the call.
  • SecurityException - can be prevented by checking permissions before making the call.

In the end, you can cover all exceptions that indicate run-time issues by catching IOException and UnauthorizedAccessException, and preventing the rest of the exceptions from happening by pre-validating the parameters you plan to pass and examining the run-time environment of your code.

Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    My issue is that this is a lot of work for simple file opening and reading. And duplicated work - because Microsoft has coded all of these in .NET. So why would I duplicate their work? – Marko May 27 '13 at 10:56
  • @Marko It's not duplicated - Microsoft did it for their own specific code path, which is different from yours. You can reuse most of their work, too, by constructing a `FileInfo` object, and seeing if you get any exceptions from the constructor. If you don't get anything, then `ReadAllText` will throw one of `IOException` or `UnauthorizedAccessException` for the same path; other exceptions would not be thrown. Put creation of `FileInfo` in your validation of the path for much shorter code. – Sergey Kalinichenko May 27 '13 at 11:06
  • Thank you, I wasn't aware of this. I still think for my concrete purposes (just reporting an error to user) it's easier to create FileException and catch just that one exception. Catching IOException and UnauthorizedAccessException seems like doubling the work every time I use ReadAllText. – Marko May 27 '13 at 11:12
3

What you're looking for is System.IO.IOException.

Inheritance hierarchy of System.IO.IOException's:

System.Object
  System.Exception
    System.SystemException
      System.IO.IOException
        System.IO.DirectoryNotFoundException
        System.IO.DriveNotFoundException
        System.IO.EndOfStreamException
        System.IO.FileLoadException
        System.IO.FileNotFoundException
        System.IO.PathTooLongException
        System.IO.PipeException

ArgumentException is notably inherited by two well known exceptions:

System.Object
  System.Exception
    System.SystemException
      System.ArgumentException
        System.ArgumentNullException
        System.ArgumentOutOfRangeException
        //...

Some typical ArithmeticException's:

System.Object
  System.Exception
    System.SystemException
      System.ArithmeticException
        System.DivideByZeroException
        System.NotFiniteNumberException
        System.OverflowException

Also notable is ThreadAbortException, that should be catched in asynchronous event delegates used in desktop applications, or also in ASP.NET when redirecting/terminating the HttpResponse.

Other exceptions are too basic to have more "specialized base exceptions". Have a look for them in the reference of System.Exception's inheritance hierarchy and in System.SystemException's inheritance hierarchy or by reflection.

metadings
  • 3,798
  • 2
  • 28
  • 37