Hantera I/O-fel i .NET
Förutom de undantag som kan utlösas i valfritt metodanrop (till exempel OutOfMemoryException när ett system är stressat eller på grund av programmerarfel NullReferenceException ) kan .NET-filsystemmetoder utlösa följande undantag:
- System.IO.IOException, basklassen för alla System.IO undantagstyper. Den genereras för fel vars returkoder från operativsystemet inte mappas direkt till någon annan undantagstyp.
- System.IO.FileNotFoundException.
- System.IO.DirectoryNotFoundException.
- DriveNotFoundException.
- System.IO.PathTooLongException.
- System.OperationCanceledException.
- System.UnauthorizedAccessException.
- System.ArgumentException, som genereras för ogiltiga sökvägstecken i .NET Framework och i .NET Core 2.0 och tidigare versioner.
- System.NotSupportedException, som genereras för ogiltiga kolon i .NET Framework.
- System.Security.SecurityException, som genereras för program som körs i begränsat förtroende och som endast saknar nödvändiga behörigheter för .NET Framework. (Fullständigt förtroende är standardvärdet för .NET Framework.)
Mappa felkoder till undantag
Eftersom filsystemet är en operativsystemresurs anropar I/O-metoder i både .NET Core- och .NET Framework-omslutningar till det underliggande operativsystemet. När ett I/O-fel inträffar i kod som körs av operativsystemet returnerar operativsystemet felinformation till .NET I/O-metoden. Metoden översätter sedan felinformationen, vanligtvis i form av en felkod, till en .NET-undantagstyp. I de flesta fall gör den detta genom att direkt översätta felkoden till motsvarande undantagstyp. den utför inte någon särskild mappning av felet baserat på kontexten för metodanropet.
I Windows-operativsystemet mappas till exempel ett metodanrop som returnerar en felkod ERROR_FILE_NOT_FOUND
för (eller 0x02) till en FileNotFoundException, och en felkod ERROR_PATH_NOT_FOUND
för (eller 0x03) mappas till en DirectoryNotFoundException.
De exakta villkor under vilka operativsystemet returnerar specifika felkoder är dock ofta odokumenterade eller dåligt dokumenterade. Därför kan oväntade undantag inträffa. Eftersom du till exempel arbetar med en katalog i stället för en fil förväntar du dig att en ogiltig katalogsökväg till DirectoryInfo konstruktorn genererar en DirectoryNotFoundException. Men det kan också kasta en FileNotFoundException.
Undantagshantering i I/O-åtgärder
På grund av detta beroende av operativsystemet kan identiska undantagsvillkor (till exempel att katalogen inte hittades i vårt exempel) leda till att en I/O-metod genererar någon av hela klassen med I/O-undantag. Det innebär att när du anropar I/O-API:er bör koden vara beredd att hantera de flesta eller alla av dessa undantag, som du ser i följande tabell:
Undantagstyp | .NET Core/.NET 5+ | .NET Framework |
---|---|---|
IOException | Ja | Ja |
FileNotFoundException | Ja | Ja |
DirectoryNotFoundException | Ja | Ja |
DriveNotFoundException | Ja | Ja |
PathTooLongException | Ja | Ja |
OperationCanceledException | Ja | Ja |
UnauthorizedAccessException | Ja | Ja |
ArgumentException | .NET Core 2.0 och tidigare | Ja |
NotSupportedException | No | Ja |
SecurityException | Nej | Endast begränsat förtroende |
Hantera IOException
Som basklass för undantag i System.IO namnområdet IOException genereras även för all felkod som inte mappas till en fördefinierad undantagstyp. Det innebär att den kan genereras av alla I/O-åtgärder.
Viktigt!
Eftersom IOException är basklassen för de andra undantagstyperna System.IO i namnområdet bör du hantera i ett catch
block när du har hanterat de andra I/O-relaterade undantagen.
Från och med .NET Core 2.1 kontrolleras dessutom sökvägens korrekthet (till exempel för att säkerställa att ogiltiga tecken inte finns i en sökväg) och körningen genererar ett undantag som mappas från en operativsystemfelkod i stället för från dess egen valideringskod. Det mest sannolika undantaget som utlöses i det här fallet är en IOException, även om alla andra undantagstyper också kan genereras.
Observera att i koden för undantagshantering bör du alltid hantera den IOException sista. Annars, eftersom det är basklassen för alla andra I/O-undantag, utvärderas inte fångstblocken för härledda klasser.
När det gäller kan IOExceptiondu få ytterligare felinformation från egenskapen IOException.HResult . Om du vill konvertera HResult-värdet till en Win32-felkod tar du bort de övre 16 bitarna av 32-bitarsvärdet. I följande tabell visas felkoder som kan vara omslutna i en IOException.
HResult | Konstant | beskrivning |
---|---|---|
ERROR_SHARING_VIOLATION | 32 | Filnamnet saknas eller så används filen eller katalogen. |
ERROR_FILE_EXISTS | 80 | Filen finns redan. |
ERROR_INVALID_PARAMETER | 87 | Ett argument som anges i metoden är ogiltigt. |
ERROR_ALREADY_EXISTS | 183 | Filen eller katalogen finns redan. |
Du kan hantera dessa med hjälp av en When
sats i en catch-instruktion, vilket visas i följande exempel.
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