Menangani kesalahan I/O di .NET
Selain pengecualian yang dapat dilemparkan dalam panggilan metode apa pun (seperti OutOfMemoryException ketika sistem menjadi stres atau NullReferenceException karena kesalahan pemrogram), metode sistem file .NET dapat melemparkan pengecualian berikut:
- System.IO.IOException, kelas dasar dari semua System.IO jenis pengecualian. Ini dilemparkan untuk kesalahan yang kode pengembaliannya dari sistem operasi tidak langsung dipetakan ke jenis pengecualian lainnya.
- System.IO.FileNotFoundException.
- System.IO.DirectoryNotFoundException.
- DriveNotFoundException.
- System.IO.PathTooLongException.
- System.OperationCanceledException.
- System.UnauthorizedAccessException.
- System.ArgumentException, yang dimunculkan untuk karakter jalur yang tidak valid pada .NET Framework dan pada .NET Core 2.0 dan versi sebelumnya.
- System.NotSupportedException, yang dimunculkan untuk titik dua yang tidak valid dalam .NET Framework.
- System.Security.SecurityException, yang dimunculkan untuk aplikasi yang berjalan dalam kepercayaan terbatas yang tidak memiliki izin yang diperlukan hanya pada .NET Framework. (Kepercayaan penuh adalah default pada .NET Framework.)
Memetakan kode kesalahan ke pengecualian
Karena sistem file adalah sumber daya sistem operasi, metode I/O di .NET Core dan .NET Framework membungkus panggilan ke sistem operasi yang mendasar. Ketika kesalahan I/O terjadi dalam kode yang dijalankan oleh sistem operasi, sistem operasi mengembalikan informasi kesalahan ke metode .NET I/O. Metode ini kemudian menerjemahkan informasi kesalahan, biasanya dalam bentuk kode kesalahan, ke dalam jenis pengecualian .NET. Dalam kebanyakan kasus, ia melakukan ini dengan langsung menerjemahkan kode kesalahan ke dalam jenis pengecualian yang sesuai; ini tidak melakukan pemetaan khusus kesalahan berdasarkan konteks panggilan metode.
Misalnya, pada sistem operasi Windows, panggilan metode yang mengembalikan kode kesalahan ERROR_FILE_NOT_FOUND
(atau 0x02) memetakan ke FileNotFoundException, dan kode kesalahan ERROR_PATH_NOT_FOUND
(atau 0x03) memetakan ke DirectoryNotFoundException.
Namun, kondisi yang tepat di mana sistem operasi mengembalikan kode kesalahan tertentu sering kali tidak terdokumentasi atau terdokumentasikan dengan buruk. Akibatnya, pengecualian tak terduga dapat terjadi. Misalnya, karena Anda bekerja dengan direktori daripada file, Anda akan mengharapkan bahwa menyediakan jalur direktori yang tidak valid ke DirectoryInfo konstruktor akan memunculkan DirectoryNotFoundException. Namun, dapat juga memunculkan FileNotFoundException.
Penanganan pengecualian dalam operasi I/O
Karena ketergantungan ini pada sistem operasi, kondisi pengecualian yang identik (seperti direktori tidak menemukan kesalahan dalam contoh kami) dapat mengakibatkan metode I/O memunculkan salah satu dari seluruh kelas pengecualian I/O. Ini berarti bahwa, saat memanggil API I/O, kode Anda harus siap untuk menangani sebagian besar atau semua pengecualian ini, seperti yang ditunjukkan dalam tabel berikut:
Jenis pengecualian | .NET Core/.NET 5+ | .NET Framework |
---|---|---|
IOException | Ya | Ya |
FileNotFoundException | Ya | Ya |
DirectoryNotFoundException | Ya | Ya |
DriveNotFoundException | Ya | Ya |
PathTooLongException | Ya | Ya |
OperationCanceledException | Ya | Ya |
UnauthorizedAccessException | Ya | Ya |
ArgumentException | .NET Core 2.0 dan yang lebih lama | Ya |
NotSupportedException | No | Ya |
SecurityException | Tidak | Kepercayaan terbatas saja |
Menangani IOException
Sebagai kelas dasar untuk pengecualian di System.IO namespace layanan, IOException juga dimunculkan untuk kode kesalahan apa pun yang tidak memetakan ke jenis pengecualian yang telah ditentukan sebelumnya. Ini berarti bahwa itu dapat dimunculkan oleh operasi I/O apa pun.
Penting
Karena IOException adalah kelas dasar dari jenis pengecualian lain di System.IO namespace layanan, Anda harus menangani dalam catch
blok setelah Anda menangani pengecualian terkait I/O lainnya.
Selain itu, dimulai dengan .NET Core 2.1, validasi memeriksa kebenaran jalur (misalnya, untuk memastikan bahwa karakter yang tidak valid tidak ada di jalur) telah dihapus, dan runtime bahasa umum memunculkan pengecualian yang dipetakan dari kode kesalahan sistem operasi daripada dari kode validasinya sendiri. Pengecualian yang paling dapat dimunculkan dalam kasus ini adalah IOException, meskipun jenis pengecualian lainnya juga dapat dimunculkan.
Perhatikan bahwa, dalam kode penanganan pengecualian Anda, Anda harus selalu menangani yang IOException terakhir. Jika tidak, karena ini adalah kelas dasar dari semua pengecualian IO lainnya, blok tangkapan kelas turunan tidak akan dievaluasi.
Dalam kasus IOException, Anda bisa mendapatkan informasi kesalahan tambahan dari properti IOException.HResult. Untuk mengonversi nilai HResult menjadi kode kesalahan Win32, Anda menghapus 16 bit atas nilai 32-bit. Tabel berikut mencantumkan kode kesalahan yang dapat dibungkus dalam IOException.
Hresult | Terus-menerus | Deskripsi |
---|---|---|
ERROR_SHARING_VIOLATION | 32 | Nama file hilang, atau file atau direktori sedang digunakan. |
ERROR_FILE_EXISTS | 80 | File sudah ada. |
ERROR_INVALID_PARAMETER | 87 | Argumen yang diberikan ke metode tidak valid. |
ERROR_ALREADY_EXISTS | 183 | File atau direktori sudah ada. |
Anda dapat menanganinya menggunakan When
klausul dalam pernyataan tangkapan, seperti yang ditunjukkan contoh berikut.
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