Bagikan melalui


Menangani kesalahan I/O di .NET

Selain pengecualian yang dapat dilemparkan dalam panggilan metode apa pun (seperti OutOfMemoryException ketika sistem stres atau NullReferenceException karena kesalahan pemrogram), metode sistem file .NET dapat melemparkan pengecualian berikut:

Memetakan kode kesalahan ke pengecualian

Karena sistem file adalah sumber daya sistem operasi, metode I/O dalam .NET Core dan .NET Framework melakukan pemanggilan ke sistem operasi yang mendasari. 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) dipetakan ke FileNotFoundException, dan kode kesalahan ERROR_PATH_NOT_FOUND (atau 0x03) dipetakan ke DirectoryNotFoundException.

Namun, kondisi yang tepat di mana sistem operasi mengembalikan kode kesalahan tertentu sering tidak terdokumentasi atau didokumentasikan dengan buruk. Akibatnya, pengecualian tak terduga dapat terjadi. Misalnya, karena Anda bekerja dengan direktori, bukan file, Anda akan mengharapkan bahwa jika menyediakan jalur direktori yang tidak valid ke konstruktor DirectoryInfo, akan menghasilkan lemparan DirectoryNotFoundException. Namun, mungkin juga melempar FileNotFoundException.

Penanganan pengecualian dalam operasi I/O

Karena ketergahan ini pada sistem operasi, kondisi pengecualian yang identik (seperti direktori tidak menemukan kesalahan dalam contoh kami) dapat mengakibatkan metode I/O melempar salah satu dari seluruh kelas pengecualian I/O. Ini berarti bahwa, saat memanggil API I/O, kode Anda harus disiapkan 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 Tidak. Ya
SecurityException Tidak. Kepercayaan terbatas saja

Menangani IOException

Sebagai kelas dasar untuk pengecualian di System.IO namespace, IOException juga dilemparkan untuk setiap kode kesalahan yang tidak sesuai dengan jenis pengecualian yang telah ditentukan sebelumnya. Ini berarti bahwa itu dapat dilemparkan oleh operasi I/O apa pun.

Penting

Karena IOException adalah kelas dasar dari jenis pengecualian lain di System.IO namespace, Anda harus menangani di catch blok setelah Anda menangani pengecualian terkait I/O lainnya.

Selain itu, dimulai dengan .NET Core 2.1, pemeriksaan validasi jalur (misalnya, untuk memastikan bahwa karakter yang tidak valid tidak ada di jalur) telah dihapus, dan runtime melemparkan pengecualian yang dipetakan dari kode kesalahan sistem operasi, bukan menggunakan kode validasinya sendiri. Pengecualian yang paling mungkin untuk dilemparkan dalam kasus ini adalah IOException, meskipun jenis pengecualian lainnya juga dapat dilemparkan.

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 ke kode kesalahan Win32, Anda menghapus 16 bit atas dari nilai 32-bit. Tabel berikut mencantumkan kode kesalahan yang mungkin dibungkus dalam IOException.

Hresult Konstanta Deskripsi
KESALAHAN_PELANGGARAN_BAGI_PAKAI 32 Nama file hilang, atau file atau direktori sedang digunakan.
KESALAHAN_FILE_ADA 80 File sudah ada.
KESALAHAN_PARAMETER_TIDAK_VALID 87 Argumen yang diberikan ke metode tidak valid.
Error_Sudah_Ada 183 File atau direktori sudah ada.

Anda dapat menanganinya menggunakan When klausul dalam pernyataan catch, 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

Lihat juga