Zpracování vstupně-výstupních chyb v .NET
Kromě výjimek, které mohou být vyvolány v jakémkoli volání metody (například OutOfMemoryException při zatížení systému nebo NullReferenceException kvůli chybě programátora), mohou metody systému souborů .NET vyvolat následující výjimky:
- System.IO.IOException, základní třída všech System.IO typů výjimek. Vyvolá se kvůli chybám, jejichž návratové kódy z operačního systému se nemapují přímo na žádný jiný typ výjimky.
- System.IO.FileNotFoundException.
- System.IO.DirectoryNotFoundException.
- DriveNotFoundException.
- System.IO.PathTooLongException.
- System.OperationCanceledException.
- System.UnauthorizedAccessException.
- System.ArgumentException, který je vyvolán kvůli neplatným znakům cesty v rozhraní .NET Framework a v .NET Core 2.0 a předchozích verzích.
- System.NotSupportedException, který je vyvolán pro neplatné dvojtečky v rozhraní .NET Framework.
- System.Security.SecurityException, který je vyvolán pro aplikace spuštěné v omezené důvěryhodnosti, které nemají potřebná oprávnění pouze pro rozhraní .NET Framework. (Úplný vztah důvěryhodnosti je výchozí v rozhraní .NET Framework.)
Mapování kódů chyb na výjimky
Vzhledem k tomu, že systém souborů je prostředek operačního systému, vstupně-výstupní metody v rozhraní .NET Core i rozhraní .NET Framework zabalí volání základního operačního systému. Pokud dojde k V/V chybě v kódu spuštěného operačním systémem, operační systém vrátí informace o chybě do V/V metody .NET. Metoda pak přeloží informace o chybě, obvykle ve formě kódu chyby, do typu výjimky .NET. Ve většině případů to dělá tak, že přímo přeloží kód chyby do odpovídajícího typu výjimky; neprovádí žádné zvláštní mapování chyby na základě kontextu volání metody.
Například v operačním systému Windows volání metody, která vrací kód ERROR_FILE_NOT_FOUND
chyby (nebo 0x02) mapuje na , FileNotFoundExceptiona kód ERROR_PATH_NOT_FOUND
chyby (nebo 0x03) mapuje na DirectoryNotFoundException.
Přesné podmínky, za kterých operační systém vrací konkrétní kódy chyb, jsou však často nezdokumentovány nebo špatně zdokumentované. V důsledku toho může dojít k neočekávaným výjimkám. Například vzhledem k tomu, že pracujete s adresářem místo souboru, byste očekávali, že poskytnutí neplatné cesty k DirectoryInfo adresáři konstruktoru DirectoryNotFoundExceptionvyvolá . Může však také vyvolat FileNotFoundException.
Zpracování výjimek v vstupně-výstupních operacích
Kvůli závislosti na operačním systému můžou identické podmínky výjimek (například v našem příkladu nenalezena chyba adresáře) vést k vyvolání vstupně-výstupní metody vyvolání jakékoli z jedné ze tříd výjimek vstupně-výstupních operací. To znamená, že při volání rozhraní API pro vstupně-výstupní operace by měl být váš kód připravený na zpracování většiny nebo všech těchto výjimek, jak je znázorněno v následující tabulce:
Typ výjimky | .NET Core/.NET 5 nebo novější | .NET Framework |
---|---|---|
IOException | Ano | Ano |
FileNotFoundException | Ano | Ano |
DirectoryNotFoundException | Ano | Ano |
DriveNotFoundException | Ano | Ano |
PathTooLongException | Ano | Ano |
OperationCanceledException | Ano | Ano |
UnauthorizedAccessException | Ano | Yes |
ArgumentException | .NET Core 2.0 a starší | Yes |
NotSupportedException | Ne | Ano |
SecurityException | No | Pouze omezený vztah důvěryhodnosti |
Zpracování ioException
Jako základní třída výjimek v System.IO oboru názvů je vyvolán také pro jakýkoli kód chyby, IOException který se nemapuje na předdefinovaný typ výjimky. To znamená, že ji může vyvolat jakákoli vstupně-výstupní operace.
Důležité
Vzhledem k tomu IOException , že je základní třídou ostatních typů výjimek v System.IO oboru názvů, měli byste zpracovat v catch
bloku po zpracování ostatních výjimek souvisejících s vstupně-výstupními operacemi.
Kromě toho, počínaje .NET Core 2.1, ověření kontroluje správnost cesty (například za účelem zajištění, že neplatné znaky nejsou přítomné v cestě) byly odebrány a modul runtime vyvolá výjimku mapovanou z kódu chyby operačního systému místo z vlastního ověřovacího kódu. Nejpravděpodobnější výjimkou, která se v tomto případě vyvolá, je IOException, i když jakýkoli jiný typ výjimky může být vyvolán.
Všimněte si, že v kódu zpracování výjimek byste měli vždy zpracovat IOException poslední. Jinak, protože se jedná o základní třídu všech ostatních výjimek vstupně-výstupních operací, bloky catch odvozených tříd nebudou vyhodnoceny.
V případě IOException, můžete získat další informace o chybě z IOException.HResult vlastnost. Chcete-li převést hodnotu HResult na kód chyby Win32, odstraňte horních 16 bitů 32bitové hodnoty. Následující tabulka uvádí kódy chyb, které mohou být zabaleny do .IOException
Hodnota HResult | Konstanta | Popis |
---|---|---|
ERROR_SHARING_VIOLATION | 32 | Název souboru chybí nebo se používá soubor nebo adresář. |
ERROR_FILE_EXISTS | 80 | Soubor již existuje. |
ERROR_INVALID_PARAMETER | 87 | Argument zadaný metodě je neplatný. |
ERROR_ALREADY_EXISTS | 183 | Soubor nebo adresář již existuje. |
Můžete je zpracovat pomocí When
klauzule v příkazu catch, jak ukazuje následující příklad.
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