Udostępnij za pośrednictwem


Obsługa błędów we/wy na platformie .NET

Oprócz wyjątków, które mogą być zgłaszane w dowolnym wywołaniu metody (np OutOfMemoryException . gdy system jest zestresowany lub NullReferenceException z powodu błędu programisty), metody systemu plików .NET mogą zgłaszać następujące wyjątki:

Mapowanie kodów błędów na wyjątki

Ponieważ system plików jest zasobem systemu operacyjnego, metody we/wy w programach .NET Core i .NET Framework opakowuje wywołania do bazowego systemu operacyjnego. Gdy w kodzie wykonywanym przez system operacyjny wystąpi błąd we/wy, system operacyjny zwraca informacje o błędzie do metody we/wy platformy .NET. Następnie metoda tłumaczy informacje o błędzie, zazwyczaj w postaci kodu błędu, na typ wyjątku platformy .NET. W większości przypadków wykonuje to przez bezpośrednie tłumaczenie kodu błędu na odpowiadający mu typ wyjątku; nie wykonuje żadnego specjalnego mapowania błędu na podstawie kontekstu wywołania metody.

Na przykład w systemie operacyjnym Windows wywołanie metody zwracające kod błędu ERROR_FILE_NOT_FOUND (lub 0x02) mapuje na element , a kod ERROR_PATH_NOT_FOUND błędu (lub 0x03) mapuje na FileNotFoundExceptionDirectoryNotFoundExceptionwartość .

Jednak dokładne warunki, w których system operacyjny zwraca określone kody błędów, są często nieudokumentowane lub źle udokumentowane. W rezultacie mogą wystąpić nieoczekiwane wyjątki. Na przykład, ponieważ pracujesz z katalogiem, a nie z plikiem, można oczekiwać, że podanie nieprawidłowej ścieżki katalogu do DirectoryInfo konstruktora zgłasza błąd DirectoryNotFoundException. Może jednak również zgłosić wartość FileNotFoundException.

Obsługa wyjątków w operacjach we/wy

Ze względu na to zależność od systemu operacyjnego identyczne warunki wyjątku (takie jak nie znaleziono katalogu w naszym przykładzie) mogą spowodować zgłoszenie dowolnej z całej klasy wyjątków we/wy. Oznacza to, że podczas wywoływania interfejsów API we/wy kod powinien być przygotowany do obsługi większości lub wszystkich tych wyjątków, jak pokazano w poniższej tabeli:

Typ wyjątku .NET Core/.NET 5+ .NET Framework
IOException Tak Tak
FileNotFoundException Tak Tak
DirectoryNotFoundException Tak Tak
DriveNotFoundException Tak Tak
PathTooLongException Tak Tak
OperationCanceledException Tak Tak
UnauthorizedAccessException Tak Tak
ArgumentException .NET Core 2.0 i starsze wersje Tak
NotSupportedException Nie Tak
SecurityException Nie. Tylko ograniczone zaufanie

Obsługa wyjątku IOException

Ponieważ klasa podstawowa wyjątków w System.IO przestrzeni nazw jest również zgłaszana dla dowolnego kodu błędu, IOException który nie jest mapowany na wstępnie zdefiniowany typ wyjątku. Oznacza to, że może zostać zgłoszony przez dowolną operację we/wy.

Ważne

Ponieważ IOException jest klasą catch bazową innych typów wyjątków w System.IO przestrzeni nazw, należy obsługiwać w bloku po obsłużeniu innych wyjątków związanych z we/wy.

Ponadto, począwszy od platformy .NET Core 2.1, sprawdzanie poprawności ścieżki (na przykład w celu upewnienia się, że nieprawidłowe znaki nie znajdują się w ścieżce), zostało usunięte, a środowisko uruchomieniowe zgłasza wyjątek zamapowany na kod błędu systemu operacyjnego, a nie z własnego kodu walidacji. Najbardziej prawdopodobnym wyjątkiem, który ma zostać zgłoszony w tym przypadku, jest IOExceptionwyjątek , chociaż można również zgłosić inny typ wyjątku.

Należy pamiętać, że w kodzie obsługi wyjątków zawsze należy obsługiwać IOException ostatnie. W przeciwnym razie, ponieważ jest to klasa bazowa wszystkich innych wyjątków we/wy, bloki catch klas pochodnych nie zostaną ocenione.

W przypadku IOExceptionelementu można uzyskać dodatkowe informacje o błędzie z właściwości IOException.HResult . Aby przekonwertować wartość HResult na kod błędu Win32, należy usunąć górne 16 bitów wartości 32-bitowej. W poniższej tabeli wymieniono kody błędów, które mogą być opakowane w element IOException.

HResult Stała opis
ERROR_SHARING_VIOLATION 32 Brak nazwy pliku lub pliku lub katalogu jest używany.
ERROR_FILE_EXISTS 80 Plik już istnieje.
ERROR_INVALID_PARAMETER 87 Argument dostarczony do metody jest nieprawidłowy.
ERROR_ALREADY_EXISTS 183 Plik lub katalog już istnieje.

Można je obsłużyć przy użyciu When klauzuli w instrukcji catch, jak pokazano w poniższym przykładzie.

using System;
using System.IO;
using System.Text;

class Program
{
    static void Main()
    {
        var sw = OpenStream(@".\textfile.txt");
        if (sw is null)
            return;
        sw.WriteLine("This is the first line.");
        sw.WriteLine("This is the second line.");
        sw.Close();
    }

    static StreamWriter? OpenStream(string path)
    {
        if (path is null)
        {
            Console.WriteLine("You did not supply a file path.");
            return null;
        }

        try
        {
            var fs = new FileStream(path, FileMode.CreateNew);
            return new StreamWriter(fs);
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("The file or directory cannot be found.");
        }
        catch (DirectoryNotFoundException)
        {
            Console.WriteLine("The file or directory cannot be found.");
        }
        catch (DriveNotFoundException)
        {
            Console.WriteLine("The drive specified in 'path' is invalid.");
        }
        catch (PathTooLongException)
        {
            Console.WriteLine("'path' exceeds the maximum supported path length.");
        }
        catch (UnauthorizedAccessException)
        {
            Console.WriteLine("You do not have permission to create this file.");
        }
        catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32)
        {
            Console.WriteLine("There is a sharing violation.");
        }
        catch (IOException e) when ((e.HResult & 0x0000FFFF) == 80)
        {
            Console.WriteLine("The file already exists.");
        }
        catch (IOException e)
        {
            Console.WriteLine($"An exception occurred:\nError code: " +
                              $"{e.HResult & 0x0000FFFF}\nMessage: {e.Message}");
        }
        return null;
    }
}
Imports System.IO

Module Program
    Sub Main(args As String())
        Dim sw = OpenStream(".\textfile.txt")
        If sw Is Nothing Then Return

        sw.WriteLine("This is the first line.")
        sw.WriteLine("This is the second line.")
        sw.Close()
    End Sub

    Function OpenStream(path As String) As StreamWriter
        If path Is Nothing Then
            Console.WriteLine("You did not supply a file path.")
            Return Nothing
        End If

        Try
            Dim fs As New FileStream(path, FileMode.CreateNew)
            Return New StreamWriter(fs)
        Catch e As FileNotFoundException
            Console.WriteLine("The file or directory cannot be found.")
        Catch e As DirectoryNotFoundException
            Console.WriteLine("The file or directory cannot be found.")
        Catch e As DriveNotFoundException
            Console.WriteLine("The drive specified in 'path' is invalid.")
        Catch e As PathTooLongException
            Console.WriteLine("'path' exceeds the maximum supported path length.")
        Catch e As UnauthorizedAccessException
            Console.WriteLine("You do not have permission to create this file.")
        Catch e As IOException When (e.HResult And &h0000FFFF) = 32
            Console.WriteLine("There is a sharing violation.")
        Catch e As IOException When (e.HResult And &h0000FFFF) = 80
            Console.WriteLine("The file already exists.")
        Catch e As IOException
            Console.WriteLine($"An exception occurred:{vbCrLf}Error code: " +
                              $"{e.HResult And &h0000FFFF}{vbCrLf}Message: {e.Message}")
        End Try
        Return Nothing
    End Function
End Module

Zobacz też