Tworzenie i zgłaszanie wyjątków

Wyjątki są używane do wskazywania, że wystąpił błąd podczas uruchamiania programu. Obiekty wyjątków opisujące błąd są tworzone, a następnie zgłaszane za pomocą instrukcji throw lub wyrażenia. Następnie środowisko uruchomieniowe wyszukuje najbardziej zgodną procedurę obsługi wyjątków.

Programiści powinni zgłaszać wyjątki, gdy spełniony jest co najmniej jeden z następujących warunków:

  • Metoda nie może ukończyć zdefiniowanej funkcji. Jeśli na przykład parametr metody ma nieprawidłową wartość:

    static void CopyObject(SampleClass original)
    {
        _ = original ?? throw new ArgumentException("Parameter cannot be null", nameof(original));
    }
    
  • Wykonano niewłaściwe wywołanie obiektu na podstawie stanu obiektu. Jednym z przykładów może być próba zapisu w pliku tylko do odczytu. W przypadkach, gdy stan obiektu nie zezwala na operację, zgłasza wystąpienie InvalidOperationException lub obiekt na podstawie wyprowadzenia tej klasy. Poniższy kod to przykład metody, która zgłasza InvalidOperationException obiekt:

    public class ProgramLog
    {
        FileStream logFile = null!;
        public void OpenLog(FileInfo fileName, FileMode mode) { }
    
        public void WriteLog()
        {
            if (!logFile.CanWrite)
            {
                throw new InvalidOperationException("Logfile cannot be read-only");
            }
            // Else write data to the log and return.
        }
    }
    
  • Gdy argument metody powoduje wyjątek. W takim przypadku należy przechwycić oryginalny wyjątek i ArgumentException utworzyć wystąpienie. Oryginalny wyjątek powinien zostać przekazany do konstruktora ArgumentException obiektu jako parametru InnerException :

    static int GetValueFromArray(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    

    Uwaga

    W poprzednim przykładzie pokazano, jak używać InnerException właściwości . Jest celowo uproszczony. W praktyce przed użyciem należy sprawdzić, czy indeks znajduje się w zakresie. Można użyć tej techniki zawijania wyjątku, gdy element członkowski parametru zgłasza wyjątek, którego nie można przewidzieć przed wywołaniem elementu członkowskiego.

Wyjątki zawierają właściwość o nazwie StackTrace. Ten ciąg zawiera nazwę metod w bieżącym stosie wywołań wraz z nazwą pliku i numerem wiersza, w którym wyjątek został zgłoszony dla każdej metody. StackTrace Obiekt jest tworzony automatycznie przez środowisko uruchomieniowe języka wspólnego (CLR) od punktu throw instrukcji, dzięki czemu wyjątki muszą zostać zgłoszone od momentu rozpoczęcia śledzenia stosu.

Wszystkie wyjątki zawierają właściwość o nazwie Message. Ten ciąg powinien być ustawiony, aby wyjaśnić przyczynę wyjątku. Informacje wrażliwe na zabezpieczenia nie powinny być umieszczane w tekście wiadomości. Oprócz Messageparametru ArgumentException zawiera właściwość o nazwie ParamName , która powinna być ustawiona na nazwę argumentu, który spowodował zgłoszenie wyjątku. W ustawieniu ParamName właściwości należy ustawić wartość value.

Metody publiczne i chronione zgłaszają wyjątki, gdy nie mogą ukończyć zamierzonych funkcji. Zgłoszona klasa wyjątku jest najbardziej specyficznym wyjątkiem, który pasuje do warunków błędu. Te wyjątki powinny być udokumentowane jako część funkcjonalności klasy, a klasy pochodne lub aktualizacje oryginalnej klasy powinny zachować to samo zachowanie w celu zapewnienia zgodności z poprzednimi wersjami.

Kwestie, których należy unikać podczas zgłaszania wyjątków

Poniższa lista zawiera rozwiązania, których należy unikać podczas zgłaszania wyjątków:

  • Nie używaj wyjątków, aby zmienić przepływ programu w ramach zwykłego wykonywania. Użyj wyjątków, aby zgłaszać i obsługiwać warunki błędów.
  • Wyjątki nie powinny być zwracane jako wartość zwracana lub parametr zamiast zgłaszana.
  • Nie zgłaszaj System.Exceptionkodu źródłowego , , System.SystemExceptionSystem.NullReferenceExceptionlub System.IndexOutOfRangeException celowo z własnego kodu źródłowego.
  • Nie twórz wyjątków, które mogą być zgłaszane w trybie debugowania, ale nie w trybie wydania. Aby zidentyfikować błędy czasu wykonywania w fazie programowania, użyj zamiast tego potwierdzenia debugowania.

Wyjątki w metodach zwracanych przez zadania

Metody zadeklarowane za pomocą async modyfikatora mają pewne specjalne zagadnienia, jeśli chodzi o wyjątki. Wyjątki zgłoszone w metodzie async są przechowywane w zwracanym zadaniu i nie pojawiają się, dopóki na przykład zadanie nie zostanie oczekiwane. Aby uzyskać więcej informacji na temat przechowywanych wyjątków, zobacz Wyjątki asynchroniczne.

Zalecamy zweryfikowanie argumentów i zgłoszenie wszelkich odpowiednich wyjątków, takich jak ArgumentException i ArgumentNullException, przed wprowadzeniem asynchronicznych części metod. Oznacza to, że te wyjątki weryfikacji powinny pojawić się synchronicznie przed rozpoczęciem pracy. Poniższy fragment kodu przedstawia przykład, w którym w przypadku zgłaszania ArgumentException wyjątków wyjątki wyjątki pojawią się synchronicznie, podczas gdy obiekt InvalidOperationException będzie przechowywany w zwróconym zadaniu.

// Non-async, task-returning method.
// Within this method (but outside of the local function),
// any thrown exceptions emerge synchronously.
public static Task<Toast> ToastBreadAsync(int slices, int toastTime)
{
    if (slices is < 1 or > 4)
    {
        throw new ArgumentException(
            "You must specify between 1 and 4 slices of bread.",
            nameof(slices));
    }

    if (toastTime < 1)
    {
        throw new ArgumentException(
            "Toast time is too short.", nameof(toastTime));
    }

    return ToastBreadAsyncCore(slices, toastTime);

    // Local async function.
    // Within this function, any thrown exceptions are stored in the task.
    static async Task<Toast> ToastBreadAsyncCore(int slices, int time)
    {
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("Putting a slice of bread in the toaster");
        }
        // Start toasting.
        await Task.Delay(time);

        if (time > 2_000)
        {
            throw new InvalidOperationException("The toaster is on fire!");
        }

        Console.WriteLine("Toast is ready!");

        return new Toast();
    }
}

Definiowanie klas wyjątków

Programy mogą zgłaszać wstępnie zdefiniowaną klasę wyjątków w System przestrzeni nazw (z wyjątkiem przypadków, gdy zostały wcześniej zanotowane) lub tworzyć własne klasy wyjątków, wyprowadzając z Exceptionklasy . Klasy pochodne powinny definiować co najmniej trzy konstruktory: jeden konstruktor bez parametrów, jeden, który ustawia właściwość komunikatu, i jeden, który ustawia zarówno właściwości , jak Message i InnerException . Na przykład:

[Serializable]
public class InvalidDepartmentException : Exception
{
    public InvalidDepartmentException() : base() { }
    public InvalidDepartmentException(string message) : base(message) { }
    public InvalidDepartmentException(string message, Exception inner) : base(message, inner) { }
}

Dodaj nowe właściwości do klasy wyjątków, gdy podane dane są przydatne do rozpoznawania wyjątku. Jeśli nowe właściwości zostaną dodane do klasy wyjątku pochodnego, ToString() należy zastąpić, aby zwrócić dodane informacje.

specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz Wyjątki i instrukcja throw w specyfikacji języka C#. Specyfikacja języka jest ostatecznym źródłem informacji o składni i użyciu języka C#.

Zobacz też