2015-08-17 8 views
9

Ich habe eine Methode, die FileStream als Eingabe verwendet. Diese Methode wird innerhalb einer for-Schleife ausgeführt.Wie kann man einen Stream von einer Methode zurückgeben, weil er weiß, dass er entsorgt werden sollte?

private void UploadFile(FileStream fileStream) 
{ 
    var stream = GetFileStream(); 
    // do things with stream 
} 

Ich habe eine andere Methode, die erstellt und gibt das Filestream:

private FileStream GetFileStream() 
{ 
    using(FileStream fileStream = File.Open(myFile, FileMode.Open)) 
    { 
     //Do something 
     return fileStream; 
    } 
} 

Nun ist die erste Methode löst ein ObjectDisposedException wenn ich versuche, das zurückgegebene Filestream zuzugreifen, wahrscheinlich, weil es schon seit ich geschlossen ist bin Verwenden Sie "using", um den Stream ordnungsgemäß zu entsorgen.

Wenn ich "using" nicht verwende und stattdessen wie folgt verwende, bleibt der FileStream offen und die nächste Iteration der Schleife (die auf der gleichen Datei läuft) löst eine Ausnahme aus, die besagt, dass die Datei bereits verwendet wird:

private FileStream GetFileStream() 
{ 
    FileStream fileStream = File.Open(myFile, FileMode.Open); 
    //Do something 
    return fileStream; 
} 

Wenn ich einen try-finally-Block verwenden, wo ich den Strom in der finally schließen sie dann auch die ObjectDisposedException wirft.

Wie effektiv Dateistrom zurück und schließen Sie es?

+2

Sie können es nicht schließen, nicht Ihre Arbeit. Benutze gute Namen. "Get" ist nicht genug, um dem Programmierer zu helfen, herauszufinden, dass er den Stream entsorgen muss, stattdessen "Create" verwenden. –

Antwort

16

Wenn Sie eine IDisposable von einer Methode zurückgeben, verweisen Sie die Verantwortung für die Entsorgung an Ihren Aufrufer. Daher müssen Sie Ihren using Block für die gesamte Verwendung des Streams deklarieren, was in Ihrem Fall vermutlich den UploadFile Aufruf umfasst.

using (var s = GetFileStream()) 
    UploadFile(s); 
4

Wenn Sie über eine Methode verfügen, die einen offenen Dateistream zurückgeben muss, müssen alle Aufrufer dieser Methode die Verantwortung für die Entsorgung des zurückgegebenen Streams übernehmen, da der Stream vor der Rückgabe nicht beseitigt werden kann.

4

Das Problem ist, dass das Filestream-Objekt wird, sobald Verlassen des Verfahren GetFileStream() angeordnet ist, ist es in einem unbrauchbaren Zustand zu verlassen. Wie andere Antworten bereits angeben, müssen Sie den using Block aus diesem Verfahren entfernen und stattdessen setzen die using Block um jeden Code, der diese Methode aufruft:

private FileStream GetFileStream() 
{ 
    FileStream fileStream = File.Open(myFile, FileMode.Open); 
    //Do something 
    return fileStream; 
} 

using (var stream = GetFileStream()) 
{ 
    UploadFile(stream); 
} 

Allerdings möchte ich dies noch einen Schritt weiter nehmen. Sie möchten eine Möglichkeit, den von Ihrem GetFileStream() erstellten Stream vor dem Fall zu schützen, dass ein schlampiger Programmierer die Methode ohne einen using Block aufrufen könnte, oder zumindest den Aufrufern deutlich anzeigen, dass das Ergebnis dieser Methode in einen using Block eingeschlossen werden muss . Daher empfehle ich folgendes:

public class FileIO : IDisposable 
{ 
    private FileStream streamResult = null; 

    public FileStream GetFileStream(string myFile) 
    { 
     streamResult = File.Open(myFile, FileMode.Open); 
     //Do something 
     return streamResult; 
    } 

    public void Dispose() 
    { 
     if (streamResult != null) streamResult.Dispose();   
    } 

} 

using (var io = FileIO()) 
{ 
    var stream = io.GetFileStream(myFile); 

    // loop goes here. 
} 

Beachten Sie, dass Sie nicht unbedingt eine ganz neue Klasse dafür erstellen müssen. Möglicherweise haben Sie bereits eine geeignete Klasse für diese Methode, bei der Sie einfach den IDisposable-Code hinzufügen können. Die Hauptsache ist, dass Sie IDisposable als Signal für andere Programmierer verwenden möchten, dass dieser Code mit einem using Block umschlossen werden soll.

Darüber hinaus können Sie damit die Klasse so ändern, dass Sie Ihr IDisposable-Objekt einmal vor der Schleife erstellen können und die neue Klasseninstanz den Überblick über alles behält, was Sie am Ende der Schleife benötigen.

+1

Dies ist eine großartige [email protected] Joel würde es dir etwas ausmachen, ein wenig darüber zu erzählen, wie "IDisposable" als Signal für andere Programmierer benutzt wird? Wie wissen andere Programmierer, wenn sie diese Klasse instanziieren, dass sie ihren Code in einen 'using'-Block schreiben sollen? – jrn