2010-10-18 8 views
11

sono stato colpito in faccia da un comportamento molto strano del metodo System.IO.Directory.GetParent:Bug in Directory.GetParent?

string path1 = @"C:\foo\bar"; 
DirectoryInfo parent1 = Directory.GetParent(path1); 
Console.WriteLine (parent1.FullName); // Prints C:\foo, as expected 

// Notice the extra backslash. It should still refer to the same location, right ? 
string path2 = @"C:\foo\bar\"; 
DirectoryInfo parent2 = Directory.GetParent(path2); 
Console.WriteLine (parent2.FullName); // Prints C:\foo\bar !!! 

riterrei un bug, ma questo metodo è stato lì dal 1.0, quindi credo che sarebbe stato rilevato da adesso. D'altra parte, se è come progettato, non riesco a pensare ad una spiegazione ragionevole per un tale progetto ...

Cosa ne pensi? E 'un errore ? Altrimenti, come spiega questo comportamento?

+0

È importante riconoscere perché il framework .NET ha * due * classi che si occupano di directory. Uno di loro indovina, l'altro verifica. Conosci la differenza. –

+0

@Hans: in realtà, né Directory né DirectoryInfo verificano nulla ... Il percorso che ho usato per il mio test in realtà non esiste sul mio filesystem, ed entrambe le classi sono perfettamente felici di affrontarlo (a patto che non lo faccia qualcosa che richiede davvero la strada per esistere, ovviamente). Ad ogni modo, apparentemente Directory indovina in errore ... IMO dovrebbe trattare entrambi i percorsi esattamente nello stesso modo –

+0

Dovrei verificare ma, sospiro, le probabilità che sia aggiustabile sono così ridotte. Non ha proprio senso Invia questo a connect.microsoft.com –

risposta

9

un po 'googling rivela some thoughts:

DirectoryInfo di = new DirectoryInfo(@"C:\parent\child"); 
Console.WriteLine(di.Parent.FullName); 

e

DirectoryInfo di = new DirectoryInfo(@"C:\parent\child\"); 
Console.WriteLine(di.Parent.FullName); 

Sia ritorno "C: \ genitore"

posso solo supporre Directory.GetParent(...) non può assumere che C:\parent\child è una directory invece di un file senza estensione di file. DirectoryInfo può, perché stai costruendo l'oggetto in questo modo.


Personalmente quello che penso è che quando c'è un backslash, la stringa viene trattato come un percorso per il file "null" all'interno della directory (cioè un file senza nome ed estensione). Apparentemente, quelli possono esistere (ci dovrebbe essere un collegamento, ma per qualche motivo non riesco a trovare nulla).

Provare a costruire un oggetto FileInfo su path2. Vedrai che è costruito correttamente, ha String.Empty come nome ed estensione, non esiste e ha C:\foo\bar come DirectoryName. Detto questo, la situazione ha senso: l'oggetto genitore per questo "file nullo" è in effetti C:\foo\bar.

+0

Sì, ho pensato a qualcosa del genere, ma non ha senso: il caso senza un backseek finale funziona bene (il genitore è "C: \ parent" se "child" è un file o una directory), è il caso * con * il backslash finale che non sta funzionando: 'c: \ parent \ child \' è ovviamente una directory, non un file (nessun percorso file termina con una barra rovesciata). E anche se * potrebbe * essere un file, il suo genitore dovrebbe essere ancora 'c: \ parent' ... –

+0

Per quanto riguarda la soluzione con' new DirectoryInfo': questo è quello che ho fatto alla fine. In realtà la mia domanda non riguardava veramente "come risolvere quel problema", ma piuttosto "perché si comporta in questo modo" –

+0

@Thomas: Forse anche no ... vedi la modifica. – GSerg

1

Questo è piuttosto interessante. Per prima cosa quando ho letto questo ero abbastanza sicuro che questo sarebbe stato un bug, ma quando ho pensato un po 'più a lungo sono giunto alla conclusione che probabilmente l'intento è che il percorso non dovrebbe essere una directory ma un percorso completo o relativo in un file. Così

c: \ somenonexistingpath \ a \ a \ directory \

verrà interpretato come un percorso di un file senza nome in ... \ directory. È un po 'sciocco, ma se supponiamo che i programmatori di Microsoft si aspettassero un percorso completo per un file, ha senso non coprire questo caso.

EDIT:

Nota che

c: \ dir \ makefile -> c: \ dir

c: \ dir \ build.msbuild -> c: \ dir

dare il genitore come previsto.

+0

Questo avrebbe senso, ma la documentazione non dice nulla sul percorso essendo un percorso di file o directory ... Presumo che loro l'avrei menzionato se il metodo si aspettava un percorso di file. E un file senza nome non avrebbe molto senso;). Ad ogni modo, penso che presenterò un bug report su Connect, solo per vedere cosa MS ha da dire al riguardo. –

+0

Un file senza nome non ha senso, ma la mia tesi è che non avevano alcuna aspettativa che le persone avrebbero assunto un percorso per una directory. Sono d'accordo che la documentazione dovrebbe essere più chiara. – steinar

3

Sono d'accordo con GSerg. Solo per aggiungere un po 'di potenza di fuoco, aggiungerò i seguenti frammenti di codice ottenuti con Reflector.

La funzione Directory.GetParent fondamentalmente chiama semplicemente la funzione Path.GetDirectoryName:

[SecuritySafeCritical] 
public static DirectoryInfo GetParent(string path) 
{ 
    if (path == null) 
    { 
     throw new ArgumentNullException("path"); 
    } 
    if (path.Length == 0) 
    { 
     throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"), "path"); 
    } 
    string directoryName = Path.GetDirectoryName(Path.GetFullPathInternal(path)); 
    if (directoryName == null) 
    { 
     return null; 
    } 
    return new DirectoryInfo(directoryName); 
} 

La proprietà Parent del DirectoryInfo strisce sostanzialmente fuori una barra finale e quindi chiama Path.GetDirectoryName:

public DirectoryInfo Parent 
{ 
    [SecuritySafeCritical] 
    get 
    { 
     string fullPath = base.FullPath; 
     if ((fullPath.Length > 3) && fullPath.EndsWith(Path.DirectorySeparatorChar)) 
     { 
      fullPath = base.FullPath.Substring(0, base.FullPath.Length - 1); 
     } 
     string directoryName = Path.GetDirectoryName(fullPath); 
     if (directoryName == null) 
     { 
      return null; 
     } 
     DirectoryInfo info = new DirectoryInfo(directoryName, false); 
     new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, info.demandDir, false, false).Demand(); 
     return info; 
    } 
} 
+0

Interessante ... Non ho nemmeno pensato di controllare con Reflector (molto diverso da me ...). Quindi il problema è in realtà con Path.GetDirectoryName, non Directory.GetParent –