Bagikan melalui


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:

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

Lihat juga