CodeGym /Kurse /C# SELF /Vergleich der Ansätze File...

Vergleich der Ansätze File vs FileInfo

C# SELF
Level 39 , Lektion 3
Verfügbar

1. Einführung

In .NET gibt es zwei philosophisch unterschiedliche Gruppen von Klassen zur Arbeit mit Dateien und Ordnern:

Statische Klassen: File, Directory — funktionieren wie ein Satz Utility-Funktionen: du rufst eine Methode auf, übergibst einen Pfad, bekommst das Ergebnis.

Instanzklassen: FileInfo, DirectoryInfo — repräsentieren konkrete Dateien und Ordner als Objekte mit eigenen Eigenschaften und Methoden.

Warum beide Ansätze? Es ist ein Kompromiss zwischen Einfachheit der Nutzung (statische Methoden) und objektorientierter Flexibilität (Instanzklassen). Jeder ist in seinen Szenarien praktisch.

2. Philosophie der statischen Klassen

Die Klassen File und Directory folgen dem Prinzip „Einfach machen!“. Sie bieten ein geradliniges Interface für Operationen am Dateisystem, ohne dass man Zwischenobjekte erstellen muss.

// Überprüfen, ob die Datei existiert
if (File.Exists("document.txt"))
{
    // Den gesamten Text lesen
    string content = File.ReadAllText("document.txt");
    
    // Ein Backup erstellen
    File.Copy("document.txt", "document_backup.txt");
    
    // Das Original löschen
    File.Delete("document.txt");
}

Das ist praktisch für einfache, einmalige Operationen: man muss nicht über Objekte, ihren Lebenszyklus oder Zustand nachdenken — man ruft einfach die benötigte Methode mit dem Dateipfad auf.

Ähnlich funktioniert Directory:

// Ordner erstellen
Directory.CreateDirectory(@"C:\MyProject\Data");

// Alle Textdateien bekommen
string[] textFiles = Directory.GetFiles(@"C:\MyProject", "*.txt");

// Überprüfen, ob der Ordner existiert
if (Directory.Exists(@"C:\Temp"))
{
    Directory.Delete(@"C:\Temp", recursive: true);
}

3. Vergleich der Ansätze an praktischen Beispielen

Beispiel 1: Einfaches Kopieren einer Datei

Statischer Ansatz:

string sourcePath = "original.txt";
string destinationPath = "copy.txt";

if (File.Exists(sourcePath))
{
    File.Copy(sourcePath, destinationPath, overwrite: true);
    Console.WriteLine("Datei kopiert");
}
else
{
    Console.WriteLine("Quelldatei nicht gefunden");
}

Instanzansatz:

var sourceFile = new FileInfo("original.txt");
var destinationFile = new FileInfo("copy.txt");

if (sourceFile.Exists)
{
    sourceFile.CopyTo(destinationFile.FullName, overwrite: true);
    Console.WriteLine("Datei kopiert");
}
else
{
    Console.WriteLine("Quelldatei nicht gefunden");
}

Hier ist der statische Ansatz kompakter. Aber was, wenn zusätzliche Informationen benötigt werden?

Beispiel 2: Kopieren mit Größenprüfung

Statischer Ansatz:

string sourcePath = "largefile.zip";
string destinationPath = "backup.zip";

if (File.Exists(sourcePath))
{
    var fileInfo = new FileInfo(sourcePath); // Man musste trotzdem ein Objekt anlegen!
    if (fileInfo.Length > 100 * 1024 * 1024) // Größer als 100 MB
    {
        Console.WriteLine($"Achtung: große Datei wird kopiert ({fileInfo.Length / 1024 / 1024} MB)");
    }
    
    File.Copy(sourcePath, destinationPath, overwrite: true);
}

Instanzansatz:

var sourceFile = new FileInfo("largefile.zip");

if (sourceFile.Exists)
{
    if (sourceFile.Length > 100 * 1024 * 1024) // Größer als 100 MB
    {
        Console.WriteLine($"Achtung: große Datei wird kopiert ({sourceFile.Length / 1024 / 1024} MB)");
    }
    
    sourceFile.CopyTo("backup.zip", overwrite: true);
}

Hier wirkt der objektorientierte Ansatz natürlicher: man arbeitet mit der Datei als Objekt und nutzt ihre Eigenschaften.

Beispiel 3: Analyse des Inhalts eines Ordners

Statischer Ansatz:

string folderPath = @"C:\Documents";

if (Directory.Exists(folderPath))
{
    string[] files = Directory.GetFiles(folderPath);
    string[] subdirs = Directory.GetDirectories(folderPath);
    
    Console.WriteLine($"Dateien: {files.Length}, Ordner: {subdirs.Length}");
    
    // Um Größen zu bekommen, braucht man trotzdem FileInfo-Objekte
    long totalSize = 0;
    foreach (string filePath in files)
    {
        var fileInfo = new FileInfo(filePath);
        totalSize += fileInfo.Length;
    }
    
    Console.WriteLine($"Gesamtgröße: {totalSize / 1024} KB");
}

Instanzansatz:

var folder = new DirectoryInfo(@"C:\Documents");

if (folder.Exists)
{
    var files = folder.GetFiles();
    var subdirs = folder.GetDirectories();
    
    Console.WriteLine($"Dateien: {files.Length}, Ordner: {subdirs.Length}");
    
    long totalSize = files.Sum(f => f.Length); // Informationen sind bereits verfügbar!
    Console.WriteLine($"Gesamtgröße: {totalSize / 1024} KB");
}

Hier gewinnt DirectoryInfo: die Collection FileInfo[] enthält bereits Metadaten.

4. Auswahlkriterien für den Ansatz

Verwende statische Klassen (File/Directory), wenn:

  • Die Operation einfach und einmalig ist. Du willst schnell Existenz prüfen, eine Datei löschen oder ihren Inhalt lesen — statische Methoden sind ideal.
// Einfaches Lesen einer Konfiguration
if (File.Exists("config.json"))
{
    string config = File.ReadAllText("config.json");
    // Verarbeitung der Konfiguration...
}
  • Keine Informationen über Dateieigenschaften benötigt werden. Wenn Größe, Zeitstempel oder Attribute nicht wichtig sind — statische Aufrufe sind kürzer und besser geeignet.
  • Du mit Pfaden arbeitest, nicht mit Dateien als Objekten. Wenn die Logik mit String-Pfaden operiert, fügen sich statische Methoden natürlicher ein.

Verwende Instanzklassen (FileInfo/DirectoryInfo), wenn:

  • Du viele Informationen über eine Datei/einen Ordner brauchst. Eigenschaften wie Length, CreationTime, Attributes sind direkt verfügbar.
var logFile = new FileInfo("application.log");
Console.WriteLine($"Größe des Logs: {logFile.Length / 1024} KB");
Console.WriteLine($"Letzte Änderung: {logFile.LastWriteTime}");
Console.WriteLine($"Ort: {logFile.Directory.FullName}");
  • Du mehrere Operationen an derselben Datei ausführst. Ein Objekt einmal erstellen — mehrfach verwenden.
var document = new FileInfo("report.docx");
if (document.Exists)
{
    var backup = document.CopyTo($"report_backup_{DateTime.Now:yyyyMMdd}.docx");
    document.MoveTo("archive/report.docx");
    Console.WriteLine($"Dokument archiviert, Kopie erstellt: {backup.Name}");
}
  • Du mit Collections von Dateien arbeitest. GetFiles() und GetDirectories() liefern Objekte mit vollständigen Informationen.
  • Objektorientierte Architektur gewünscht ist. Dateien/Ordner als Objekte lassen sich leichter übergeben, speichern und in LINQ verwenden.

5. Performance-Eigenschaften und Caching

Caching in den Instanzklassen

Eine der Schlüssel-Eigenschaften von FileInfo/DirectoryInfo ist das Caching von Metadaten. Beim ersten Zugriff auf eine Eigenschaft (z.B. Length oder CreationTime) macht .NET einen Systemaufruf, lädt alle Informationen und cached sie im Objekt.

var file = new FileInfo("document.txt");

// Erster Zugriff - Systemaufruf zum Laden aller Metadaten
long size = file.Length;

// Weitere Zugriffe verwenden den Cache - sehr schnell
DateTime created = file.CreationTime;
DateTime modified = file.LastWriteTime;
bool readOnly = file.IsReadOnly;

Wenn du mehrere Eigenschaften einer Datei brauchst, ist der objektorientierte Ansatz effizienter: ein Systemaufruf statt vieler.

Problem: veralteter Cache

Die Kehrseite ist, dass die Information veraltet sein kann, wenn die Datei außerhalb deines Programms geändert wurde. Verwende Refresh():

var file = new FileInfo("data.txt");
Console.WriteLine($"Größe: {file.Length}"); // Zum Beispiel 1000 Bytes

// In der Zwischenzeit ändert ein anderes Programm die Datei...

Console.WriteLine($"Größe: {file.Length}"); // Immer noch 1000 Bytes aus dem Cache!

// Erzwinge ein Update
file.Refresh();
Console.WriteLine($"Größe: {file.Length}"); // Jetzt aktueller Wert

Statische Aufrufe gehen immer direkt zum Dateisystem, daher haben sie dieses Problem nicht.

Massenoperationen

Bei großen Dateimengen beeinflusst die Wahl des APIs die Performance:

// Statischer Ansatz — viele Systemaufrufe
string[] files = Directory.GetFiles(@"C:\Photos");
foreach (string filePath in files)
{
    var info = new FileInfo(filePath); // Systemaufruf für jede Datei
    if (info.Length > 10 * 1024 * 1024) // Größer als 10 MB
    {
        Console.WriteLine($"Großes Foto: {info.Name}");
    }
}

// Instanzansatz — ein Systemaufruf pro Ordner
var photosDir = new DirectoryInfo(@"C:\Photos");
foreach (var file in photosDir.GetFiles()) // Informationen werden sofort geladen
{
    if (file.Length > 10 * 1024 * 1024)
    {
        Console.WriteLine($"Großes Foto: {file.Name}");
    }
}

6. Unterschiede im API und Möglichkeiten

Einzigartige Möglichkeiten der Instanzklassen

var file = new FileInfo(@"C:\Projects\MyApp\source\Program.cs");

// Navigation in der Ordnerhierarchie
DirectoryInfo projectDir = file.Directory.Parent; // MyApp
DirectoryInfo sourceDir = file.Directory; // source

// Detailinformationen zur Datei
Console.WriteLine($"Erweiterung: {file.Extension}");
Console.WriteLine($"Nur-Lesen: {file.IsReadOnly}");
Console.WriteLine($"Attribute: {file.Attributes}");

// Arbeiten mit dem Verzeichnis als Objekt
var dir = new DirectoryInfo(@"C:\Projects");
DirectoryInfo parent = dir.Parent; // C:\
DirectoryInfo root = dir.Root;     // C:\

Einzigartige Möglichkeiten der statischen Klassen

// Text in einer Zeile lesen und schreiben
string content = File.ReadAllText("config.txt");
File.WriteAllText("output.txt", "Hello World");

// Arbeiten mit Zeilen
string[] lines = File.ReadAllLines("data.txt");
File.WriteAllLines("output.txt", new[] { "Line 1", "Line 2" });

// Text an Datei anhängen
File.AppendAllText("log.txt", $"{DateTime.Now}: Application started\n");

// Arbeiten mit Bytes
byte[] data = File.ReadAllBytes("image.jpg");
File.WriteAllBytes("copy.jpg", data);

7. Praktische Empfehlungen

Szenario 1: Backup-Utility

Zum Kopieren mit Prüfung von Größen und Daten sind Instanzklassen praktischer:

public void BackupDirectory(string sourcePath, string backupPath)
{
    var sourceDir = new DirectoryInfo(sourcePath);
    var backupDir = new DirectoryInfo(backupPath);
    
    if (!backupDir.Exists)
        backupDir.Create();
    
    foreach (var file in sourceDir.GetFiles())
    {
        var backupFile = new FileInfo(Path.Combine(backupPath, file.Name));
        
        // Wir kopieren nur, wenn die Datei neuer ist oder nicht existiert
        if (!backupFile.Exists || file.LastWriteTime > backupFile.LastWriteTime)
        {
            file.CopyTo(backupFile.FullName, overwrite: true);
            Console.WriteLine($"Kopiert: {file.Name} ({file.Length / 1024} KB)");
        }
    }
}

Szenario 2: Einfache Arbeit mit Textdateien

Für Laden/Speichern einfacher Daten reichen die statischen Methoden:

public void SaveUserPreferences(string username, string theme, bool notifications)
{
    string configPath = "user.config";
    string[] settings = {
        $"Username={username}",
        $"Theme={theme}",
        $"Notifications={notifications}"
    };
    
    File.WriteAllLines(configPath, settings);
}

public Dictionary<string, string> LoadUserPreferences()
{
    string configPath = "user.config";
    var preferences = new Dictionary<string, string>();
    
    if (!File.Exists(configPath))
    {
        // Erstelle Standard-Einstellungen
        SaveUserPreferences("User", "Light", true);
        return LoadUserPreferences();
    }
    
    string[] lines = File.ReadAllLines(configPath);
    foreach (string line in lines)
    {
        if (line.Contains('='))
        {
            string[] parts = line.Split('=', 2);
            preferences[parts[0]] = parts[1];
        }
    }
    
    return preferences;
}

Szenario 3: Analyse des Dateisystems

Für Berichte über die Festplattennutzung sind Instanzklassen ideal:

public void AnalyzeDiskUsage(string path)
{
    var directory = new DirectoryInfo(path);
    var report = new Dictionary<string, long>();
    
    foreach (var file in directory.GetFiles("*", SearchOption.AllDirectories))
    {
        string extension = file.Extension.ToLower();
        if (string.IsNullOrEmpty(extension))
            extension = "(ohne Erweiterung)";
            
        if (!report.ContainsKey(extension))
            report[extension] = 0;
            
        report[extension] += file.Length;
    }
    
    var sortedReport = report.OrderByDescending(kvp => kvp.Value);
    foreach (var item in sortedReport.Take(10))
    {
        Console.WriteLine($"{item.Key}: {item.Value / 1024 / 1024} MB");
    }
}

8. Typische Fehler und Fallstricke

Fehler: Mischen der Ansätze ohne Not

Manche starten mit statischen Methoden und erstellen dann Objekte für Metadaten — das führt zu unnötiger Duplizierung von Zugriffen:

// Ineffizient - zwei Zugriffe aufs Dateisystem
if (File.Exists("document.txt"))
{
    var fileInfo = new FileInfo("document.txt"); // Doppelte Existenzprüfung
    Console.WriteLine($"Größe: {fileInfo.Length}");
}

// Besser: sofort den objektorientierten Ansatz nutzen
var fileInfo = new FileInfo("document.txt");
if (fileInfo.Exists)
{
    Console.WriteLine($"Größe: {fileInfo.Length}");
}

Fehler: Ignorieren des veralteten Caches

Bei lang laufenden Prozessen mit Dateien, die extern geändert werden können, aktualisiere den Cache mit Refresh():

var logFile = new FileInfo("application.log");

while (true)
{
    logFile.Refresh(); // Aktualisiere Dateiinformationen
    
    if (logFile.Length > 100 * 1024 * 1024) // 100 MB
    {
        // Log archivieren
        logFile.MoveTo($"logs/archived_{DateTime.Now:yyyyMMdd_HHmmss}.log");
        break;
    }
    
    Thread.Sleep(60000); // Jede Minute prüfen
}

Fehler: Falsche Wahl für Massenoperationen

Bei der Verarbeitung von tausenden Dateien ist die API-Wahl kritisch für die Performance:

// Langsam - viele kleine Zugriffe aufs Dateisystem
string[] allFiles = Directory.GetFiles(@"C:\BigFolder", "*", SearchOption.AllDirectories);
var largeFiles = allFiles.Where(path => new FileInfo(path).Length > 1024 * 1024).ToList();

// Schneller - benutze vorliegende Informationen
var folder = new DirectoryInfo(@"C:\BigFolder");
var largeFiles = folder.GetFiles("*", SearchOption.AllDirectories)
                      .Where(file => file.Length > 1024 * 1024)
                      .ToList();
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION