Sollte meine Methode eine eigene Ausnahme auslösen oder .NET auslösen lassen, wenn keine Datei vorhanden ist?

75

Hier ist mein Code:

public void ReadSomeFile(string filePath)
{
    if (!File.Exists(filePath))
        throw new FileNotFoundException();

    var stream = new FileStream(filePath, ....)
    .....
}

Soll ich selbst eine Ausnahme auslösen (siehe File.ExistsScheck)? FileStreamwird bereits werfen, FileNotFoundExceptionwenn die Datei nicht existiert. Was ist hier gute Programmierpraxis? Die Code-Analyse besagt, dass wir unsere Parameter validieren sollten. Aber wenn ich diesen Parameter direkt an eine andere Methode (meinen oder einen anderen Code) übergebe und diese Methode selbst eine Ausnahme auslöst, was ist dann der Vorteil der Validierung von Argumenten in meinem Code?

fhnaseer
quelle
25
Es macht keinen Sinn, ein zu werfen FileNotFoundException- tatsächlich lädt es nur zu Problemen mit den Rennbedingungen ein. Entweder Sie behandeln die Ausnahme, lassen sie weitergeben oder schließen sie in Ihre eigene Ausnahme ein. Dies entspricht "Ich weiß, was ich damit machen soll", "Ich weiß nicht, was ich damit machen soll" bzw. "Ich möchte das höher auf dem Stapel behandeln".
Luaan
Randnotiz: Wenn Sie Argumente wirklich validieren müssen, können Sie überprüfen, ob sie filePathgültig aussehen (dh absoluter Pfad oder zumindest nicht enthalten Path.GetInvalidFileNameChars())
Alexei Levenkov
2
@AlexeiLevenkov Ich denke, das ist auch nicht erforderlich. FileStream wird das erledigen,
fhnaseer
3
Verwandte: Aus Eric Lipperts Blog - Ärgerliche Ausnahmen - Dieser Fall steht unter "exogene Ausnahmen" .
Kobi
Das hängt wirklich davon ab, was Sie erreichen möchten. es gibt also keine "richtige Antwort" darauf. es hängt davon ab
swe

Antworten:

118

if (File.Exists(f)) { DoSomething(f) }(oder die Negation davon) ist ein Anti-Muster. Die Datei kann zwischen diesen beiden Anweisungen gelöscht oder erstellt werden, daher ist es wenig sinnvoll, ihre Existenz so zu überprüfen.

Abgesehen davon kann, wie in den Kommentaren ausgeführt, File.Exists()das tatsächliche Öffnen der Datei aus verschiedenen Gründen immer noch fehlschlagen , obwohl dies möglicherweise true zurückgibt. Sie müssen also die Fehlerprüfung wiederholen und das Öffnen der Datei umgehen.

Da Sie sich nicht wiederholen möchten, sondern Ihren Code trocken halten möchten, versuchen Sie einfach, die Datei zu öffnen und new FileStream()werfen zu lassen . Dann können Sie die Ausnahme abfangen und, wenn Sie möchten, das Original erneut auslösen oder eine anwendungsspezifische Ausnahme auslösen.

Natürlich File.Exists()kann ein Anruf gerechtfertigt sein, aber nicht in diesem Muster.

CodeCaster
quelle
3
@ CodeCaster Sicher. Und ein anderer Prozess kann die Datei zwischen dem File.Open(f);Werfen und dem Abfangen erstellen . Das Problem ist weniger, dass sich die Situation ändern könnte, bevor Sie damit umgehen (da dies im Fehlerfall nahezu unvermeidbar ist), sondern dass Sie die Fehlerprüfung einmal durchführen sollten, um Wiederholungen zu vermeiden (und Sie müssen die Fehlerprüfung trotzdem beim Öffnen durchführen). (Beachten Sie auch, dass ein erfolgreiches Öffnen bei Gesprächen mit einigen Dateisystemen nicht bedeutet, dass die Datei nicht auf Ihnen gelöscht wird. Selbst dort kann der Fehler auftreten, nachdem das Öffnen zu funktionieren scheint.)
Yakk - Adam Nevraumont
2
Neben der Racebedingung ignoriert dieser Code Situationen, in denen eine Datei vorhanden ist, aber aufgrund von Berechtigungen usw. nicht lesbar ist , und möglicherweise sogar noch mehr Fehler.
el.pescado
8
Während die Überprüfung der Existenz einer Datei und der sofortige Versuch, sie zu öffnen, im Allgemeinen ein Anti-Pattern ist, würde ich sie nicht als Anti-Pattern betrachten, wenn eine andere Logik die Prüfung und das Öffnen trennt. Auf einigen Netzwerksystemen ist die Überprüfung der offensichtlichen Verfügbarkeit einer Datei möglicherweise billiger als der Erwerb der zum Öffnen erforderlichen Sperre. Wenn Code nur dann nützlich sein kann, wenn mehrere Dateien verfügbar sind, kann es hilfreich sein, sicherzustellen, dass alle Dateien verfügbar zu sein scheinen, bevor Sie die Sperren erwerben, die zum Öffnen einer dieser Dateien erforderlich sind.
Supercat
3
Python hat eine Richtlinie mit dem Namen "Einfacher um Vergebung zu bitten als um Erlaubnis" (EAFP). Es bedeutet, dass es manchmal aufgrund von Rennbedingungen und Dingen, die außerhalb Ihrer Kontrolle liegen, am besten ist, Ihr Programm zu bitten, das zu tun, was es tun möchte, und alle möglichen Fehler dort (Ausnahmen) zu behandeln, anstatt bei jedem Schritt zu überprüfen, ob die Bedingungen sind optimal. In diesem Beispiel denke ich, dass EAFP für Best Practices angewendet werden kann.
Sleblanc
1
Es kann auch eine FileNotFoundException auftreten, weil der Benutzer keine Berechtigung zum Anzeigen hat oder der Ordner nicht vorhanden ist. Wenn Sie dem Benutzer also helfen möchten, können Sie im Catch nach genaueren Gründen suchen, z. B. Folder.Exists - oder stellen Sie einfach sicher, dass in Ihrer freundlichen Benutzermeldung "Datei kann nicht gefunden oder abgerufen werden" steht!
AndrewD
15

Ihre Methode wird aufgerufen ReadSomeFileund nimmt a filenameals Eingabe, daher ist es sinnvoll, a zu werfen FileNotFoundException. Da Sie keinen Wert hinzufügen können, indem Sie die Ausnahme abfangen und dann selbst auslösen, lassen Sie .NET sie einfach auslösen.

Wenn Ihre Methode jedoch LoadData(databaseName)auf viele Dateien zugreifen muss, kann das Abfangen der Ausnahme und das Auslösen einer benutzerdefinierten Ausnahme von Wert sein, da Sie databaseNamedie Ausnahme zusammen mit anderen hilfreichen Informationen zur Ausnahme hinzufügen können.

Ian Ringrose
quelle
Und selbst im letzten Fall sollten Sie versuchen, die innere Ausnahme abzufangen, anstatt File.Exists zu verwenden.
Stig Hemmer
Es ist der Fall, wenn die Antwort, die die Frage tatsächlich beantwortet, viel weniger positive
Stimmen
1

Abgesehen von den bereits gegebenen Antworten können Sie auch sagen, dass dies davon abhängt, was Sie erwarten.

Wenn Sie eine Protokolldatei lesen möchten und diese nicht vorhanden ist, möchten Sie einen Fehler oder nur einen leeren String (oder ein leeres String-Array) auslösen?

Wenn ich einen Standardwert zurückgeben würde (wie eine leere Zeichenfolge), würde ich einfach den Inhalt der Funktion in a einschließen try-catch(aber nur für erwartete Fehler) und den Standardwert im catchBlock zurückgeben, während ich den tatsächlichen Inhalt im tryBlock zurückgeben würde.

Dies würde drei mögliche Situationen hinterlassen:

  1. Der Inhalt einer Datei wird zurückgegeben.
  2. Der Standardwert wird zurückgegeben, da ein erwarteter Fehler aufgetreten ist.
  3. Ein Fehler wird von .NET ausgelöst, da Sie diesen bestimmten Fehler nicht abgefangen haben.
Stephan Bijzitter
quelle
0

Lassen Sie die richtige Methode versuchen, die Datei zu öffnen, während Sie keine Ahnung vom vollständigen Dateinamen haben, z. B. spezielle Dateinamen (z. B. Gerätedateien und UNC-Pfade ):

In einigen Fällen können andere Dateimethoden fehlschlagen, das Öffnen der Datei ist jedoch erfolgreich.

Einige Beispiele für spezielle Dateinamen sind:

  • CON
  • NUL
  • COM1, COM2, COM3, COM4
  • \\ Server \ Freigabe \ Dateipfad
  • \\ teela \ admin $ \ system32 (um C: \ WINNT \ system32 zu erreichen)
  • C: .. \ File.txt
  • \\. \ COM1
  • % TEMP%
  • und mehr...
Amir Saniyan
quelle
2
Ich weiß nicht viel über Windows, aber ich bin mir ziemlich sicher, dass% TEMP% eine Umgebungsvariable ist, keine Datei, und dass die Funktion "Datei öffnen" sie nicht erweitert.
Functino