Em C#, um caminho é apenas uma string, então nada nele nos diz se ele aponta para um arquivo, para um diretório ou para nada. Quem responde a isso é o sistema de arquivos, e o .NET nos dá três formas de perguntar: File.Exists() e Directory.Exists(), a propriedade Exists de FileInfo e DirectoryInfo, e os bits de atributo por trás de File.GetAttributes().
Path.Exists() responde à pergunta mais curta, “existe alguma coisa aqui?”, com uma única chamada desde o .NET 7. Vamos ver cada uma delas, o que retornam quando o caminho não existe e qual escolher.
Como verificar se um arquivo ou diretório existe em C#?
File.Exists() responde a uma pergunta: este caminho aponta para um arquivo existente? Directory.Exists() responde à mesma pergunta para diretórios. Os dois são métodos estáticos que recebem uma string de caminho e retornam um bool.
Usados juntos, eles determinam se um caminho é um arquivo ou um diretório. File.Exists() retorna false quando o caminho descreve um diretório, e Directory.Exists() retorna false quando ele descreve um arquivo. Assim, para um caminho que existe, exatamente um dos dois é true, a menos que um erro esconda o caminho dos dois.
Nenhum dos dois métodos lança exceção. Segundo o Microsoft Learn, Exists “retorna false se ocorrer algum erro ao tentar determinar se o arquivo especificado existe”, o que abrange um caminho null, um caminho inválido, um disco ausente e um caminho que não temos permissão para ler. Um false significa “não é isso, ou não conseguimos saber”.
A diferenciação entre maiúsculas e minúsculas segue o sistema de arquivos: por padrão, o NTFS não diferencia; os sistemas de arquivos do Linux, sim.
Os dois métodos recebem um caminho, a string que localiza um recurso no sistema de arquivos, e o .NET tem um segundo par de classes que responde às mesmas perguntas a partir de um objeto: FileInfo e DirectoryInfo. Para ver em detalhes como os dois pares diferem, consulte os nossos artigos sobre a comparação entre as classes File e FileInfo em C# e sobre o gerenciamento de diretórios com Directory e DirectoryInfo em C#. Se esses tipos são novidade para você, comece por uma introdução às classes File e Directory desde o básico.
Uso das classes Directory e File
File e Directory são classes estáticas, então não precisamos criar instâncias delas.
As duas classes têm um método Exists(). Vamos criar um arquivo para ver como esses métodos funcionam:
var testFile = Path.Combine(Path.GetTempPath(), "test_file.abc"); File.WriteAllText(testFile, string.Empty); bool isFile = File.Exists(testFile); bool isDirectory = Directory.Exists(testFile);
Com File.Exists(), verificamos se o caminho existe como arquivo, e o método retorna true. Também usamos Directory.Exists() para verificar se test_file.abc existe como diretório, e o resultado é false.
Vamos tentar com um diretório:
var testDirectory = Path.Combine(Path.GetTempPath(), "test_directory"); Directory.CreateDirectory(testDirectory); bool isFile = File.Exists(testDirectory); bool isDirectory = Directory.Exists(testDirectory);
Desta vez, Directory.Exists() retorna true, enquanto File.Exists() retorna false.
Agora, verificamos as mesmas funções quando não existe nem um diretório nem um arquivo:
var notExistingPath = "someNotExistingPath"; bool isFile = File.Exists(notExistingPath); bool isDirectory = Directory.Exists(notExistingPath);
Tanto File.Exists() quanto Directory.Exists() retornam false.
Uso das classes DirectoryInfo e FileInfo
FileInfo e DirectoryInfo funcionam de forma parecida com File e Directory, mas não são estáticas. Por isso, precisamos criar uma instância delas, passando o caminho para o construtor.
Quando usamos FileInfo e DirectoryInfo, o acesso ao arquivo acontece apenas uma vez: na primeira leitura de uma propriedade como Exists. Então, se precisarmos fazer muitas operações com o caminho, é mais conveniente usar FileInfo e DirectoryInfo do que File e Directory. Estas últimas acessam o arquivo a cada chamada de método.
De qualquer forma, precisamos ter cuidado ao usar FileInfo e DirectoryInfo, porque elas armazenam as informações em cache. Se criarmos uma instância do objeto FileInfo e lermos Exists antes de criar o arquivo com um método de File, a propriedade Exists retorna false mesmo depois da criação do arquivo. Evitamos isso usando o método Refresh().
Vamos reproduzir o problema:
var path = Path.Combine(Path.GetTempPath(), "test_file3.abc"); if (File.Exists(path)) File.Delete(path); var fileInfo = new FileInfo(path); bool firstAnswer = fileInfo.Exists; // false, and now cached File.WriteAllText(path, string.Empty); bool staleAnswer = fileInfo.Exists; // still false fileInfo.Refresh(); bool freshAnswer = fileInfo.Exists; // true
A primeira leitura preenche o cache, então a segunda leitura ainda retorna false, embora o arquivo já exista nesse momento. Exists só informa true depois de Refresh().

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
FileInfo e DirectoryInfo têm uma propriedade Exists que responde à mesma pergunta que o método Exists() de File e Directory.
Vamos usar FileInfo e DirectoryInfo para verificar a existência de um arquivo:
var testFile = Path.Combine(Path.GetTempPath(), "test_file2.abc"); File.WriteAllText(testFile, string.Empty); var fileInfo = new FileInfo(testFile); var directoryInfo = new DirectoryInfo(testFile); bool isFile = fileInfo.Exists; bool isDirectory = directoryInfo.Exists;
Ao verificar testFile, recebemos os resultados esperados: fileInfo.Exists retorna true, enquanto directoryInfo.Exists retorna false.
Lidar com diretórios é bem simples:
var testDirectory = Path.Combine(Path.GetTempPath(), "test_directory2"); Directory.CreateDirectory(testDirectory); var fileInfo = new FileInfo(testDirectory); var directoryInfo = new DirectoryInfo(testDirectory); bool isFile = fileInfo.Exists; bool isDirectory = directoryInfo.Exists;
Com testDirectory, fileInfo.Exists retorna false e directoryInfo.Exists retorna true.
Vejamos o que fazer quando não existe nem um diretório nem um arquivo:
var notExistingPath = "someNotExistingPath2"; var fileInfo = new FileInfo(notExistingPath); var directoryInfo = new DirectoryInfo(notExistingPath); bool isFile = fileInfo.Exists; bool isDirectory = directoryInfo.Exists;
Nesse caso, tanto fileInfo.Exists quanto directoryInfo.Exists retornam false, porque notExistingPath não existe nem como arquivo nem como diretório.
Como verificar se um caminho existe em C#?
Path.Exists() responde à pergunta mais curta: existe alguma coisa neste caminho, seja arquivo ou diretório? O método recebe uma string de caminho anulável, retorna um bool e chegou no .NET 7.
Uma única chamada substitui o par File.Exists(path) || Directory.Exists(path) que os leitores escrevem à mão, e ela consulta o sistema de arquivos uma vez, em vez de até duas.
O Microsoft Learn observa que Path.Exists() retorna true para arquivos não regulares existentes, como pipes, e true para um link cujo destino não existe mais.
O Microsoft Learn desaconselha usar File.Exists() para validar caminhos, e o motivo também vale aqui: um caminho malformado retorna false, a mesma resposta dada por um caminho perfeitamente válido que não aponta para nada. Verificar a string em si é outra tarefa, e ela começa por Path.GetInvalidPathChars().
Nenhuma dessas verificações está livre de condição de corrida: outro processo pode criar ou excluir o caminho entre a nossa verificação e a próxima chamada.
Vamos fazer as três perguntas sobre o mesmo caminho:
var path = Path.Combine(Path.GetTempPath(), "test_directory"); bool anythingHere = Path.Exists(path); bool isDirectory = Directory.Exists(path); bool isFile = File.Exists(path);
Path.Exists() é true sempre que qualquer uma das outras duas também for.
Os caracteres que um caminho não pode conter são assunto do nosso artigo sobre a classe Path e seus métodos auxiliares para caracteres inválidos.
Como diferenciar um arquivo de um diretório com FileAttributes?
File.GetAttributes() lê os bits de atributo de um caminho em uma única chamada, e FileAttributes.Directory é o bit que resolve a nossa questão. Ele está definido para um diretório e não está definido para um arquivo, então attributes.HasFlag(FileAttributes.Directory) classifica qualquer coisa que exista e que possamos ler.
O problema está no que acontece quando o caminho não existe. File.GetAttributes() lança uma exceção, e a exceção que recebemos depende de qual parte do caminho está faltando: FileNotFoundException quando só o último segmento não existe, DirectoryNotFoundException quando um diretório acima dele também não existe. As duas derivam de IOException, então capturar o tipo base cobre as duas, e capturar apenas FileNotFoundException deixa uma brecha.
A forma mais limpa é parar de usar exceções para controlar o fluxo. Pergunte primeiro a Path.Exists() e leia os atributos apenas quando a resposta for true. Isso transforma duas rotas de exceção em uma única ramificação, com a mesma quantidade de código.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
Vejamos como usar File.GetAttributes():
var testFile = Path.Combine(Path.GetTempPath(), "test_file4.abc"); File.WriteAllText(testFile, string.Empty); var attributes = File.GetAttributes(testFile); bool isDirectory = attributes.HasFlag(FileAttributes.Directory); bool isFile = !isDirectory;
Se o caminho existe, o indicador FileAttributes.Directory nos diz que ele é um diretório. Se esse indicador não estiver definido, com certeza o caminho é um arquivo.
A enumeração FileAttributes também nos diz se o arquivo é um arquivo normal, se está oculto, se é somente leitura, se está compactado e muitas outras coisas. Podemos encontrar todos os detalhes lendo FileAttributes Enum no Microsoft Learn.
Quando o caminho não existe, File.GetAttributes() lança uma exceção, então perguntamos primeiro a Path.Exists():
var path = Path.Combine(Path.GetTempPath(), "someNotExistingPath");
bool isDirectory = false;
bool isFile = false;
if (Path.Exists(path))
{
var attributes = File.GetAttributes(path);
isDirectory = attributes.HasFlag(FileAttributes.Directory);
isFile = !isDirectory;
}
Para um caminho inexistente, os dois indicadores continuam false, e File.GetAttributes() nunca é executado, então não há exceção para capturar.
A Microsoft documenta FileAttributes.Directory como compatível com Windows, Linux e macOS. Vários outros indicadores da enumeração, entre eles Archive e Compressed, não têm suporte nenhum no Linux nem no macOS, então consulte a documentação da enumeração antes de depender de qualquer indicador além deste.
Qual verificação usar para um caminho em C#?
Quando a única pergunta é se existe alguma coisa ali, use Path.Exists(). Ele nunca lança exceção e, no .NET 7 e em versões posteriores, uma única chamada é a resposta correta mais curta.
Para diferenciar um diretório de um arquivo, chame Directory.Exists() e File.Exists(). Juntos, eles classificam qualquer caminho como diretório, arquivo ou nenhum dos dois, e nenhuma das duas chamadas lança exceção.
FileInfo e DirectoryInfo compensam quando vamos fazer mais com o caminho do que apenas perguntar sobre ele. As duas armazenam a resposta em cache na primeira vez que lemos uma propriedade como Exists, então essa resposta pode ficar desatualizada, e Refresh() é o que limpa esse cache. Esse cache é um custo quando queremos responder a uma única pergunta e uma economia quando queremos responder a dez.
Use File.GetAttributes() quando os outros bits de atributo também importam, como oculto e somente leitura, e proteja a chamada, porque ela lança uma exceção quando o caminho não existe.
A tabela coloca as seis chamadas lado a lado, com o que cada uma retorna para um caminho inexistente e para um diretório:
| Chamada | Pergunta que responde | Caminho inexistente | Diretório como argumento | Disponível desde |
|---|---|---|---|---|
File.Exists(path) | Este é um arquivo existente? | false | false | .NET Framework 1.0 |
Directory.Exists(path) | Este é um diretório existente? | false | true | .NET Framework 1.0 |
Path.Exists(path) | Existe alguma coisa aqui, arquivo ou diretório? | false | true | .NET 7 |
new FileInfo(path).Exists | Este é um arquivo existente? | false | false | .NET Framework 1.0 |
new DirectoryInfo(path).Exists | Este é um diretório existente? | false | true | .NET Framework 1.0 |
File.GetAttributes(path) | Quais bits de atributo estão definidos, incluindo Directory? | lança exceção | retorna atributos com Directory definido | .NET Framework 1.0 |
Todas as chamadas da tabela consultam o sistema de arquivos real, o que torna o código ao redor difícil de cobrir com testes unitários. Mostramos como testar código que acessa o sistema de arquivos sem tocar no disco em um artigo separado.
A figura coloca as duas verificações Exists() em um mesmo eixo, e o ramo âmbar na parte de baixo é o caso em que as duas retornam false:

Conclusão
Em resumo, mostramos três formas de testar se um caminho é um arquivo ou um diretório em C#. Podemos verificar se um caminho existe como arquivo ou diretório com as classes File, Directory, FileInfo e DirectoryInfo. Depois, vimos outra possibilidade usando FileAttributes. Podemos obter FileAttributes do caminho e verificar se o indicador Directory está definido.
Depois que sabemos o que um caminho é, geralmente surgem duas perguntas: como contar os arquivos dentro dele, quando é um diretório, e se outro processo está com o arquivo aberto, quando é um arquivo.
Testado com .NET 10.0.10.