4
\$\begingroup\$

Simple class to simpify runtime file loading of ressources. The class reads all files from a given directory including all subdirectories and stores the name together with the path in a simple struct. If a file needs to be used by the program just pass the name to GetPath() and the class returns the location of the file.

Any suggestions/improvements about my implementation of this? Im especially not sure if making this static is the right choice.

using System.IO;
using System.Collections.Generic;
namespace Sample
{
 struct File
 {
 public string Name;
 public string Path; 
 }
 static class FileManager
 { 
 private static List<File> Files = new List<File>(); 
 public static void AddFiles(string directory)
 {
 foreach(string file in Directory.GetFiles(directory))
 { 
 Files.Add(new File() { Name = Path.GetFileName(file), Path = directory });
 }
 foreach(string subdirectory in Directory.GetDirectories(directory))
 {
 AddFiles(subdirectory);
 }
 }
 public static string GetPath(string filename)
 {
 var File = Files.Find(x => x.Name == filename);
 return File.Path;
 }
 public static void ClearFiles()
 {
 Files.Clear();
 } 
 } 
}
asked Apr 12, 2019 at 8:31
\$\endgroup\$
3
  • 1
    \$\begingroup\$ What is Find and how does it handle files with same names? Please add its implementation to the question. Oh, or not... I see it's a List's method haha, I've never used it before ;-] and it returns the first match. Interesting. Is this what you want? Why don't you use FirstOrDefault which I find much cleaner because you clearly see what you'll get. \$\endgroup\$ Commented Apr 12, 2019 at 9:11
  • 1
    \$\begingroup\$ There should not be ressources with identical name and file ending. So Find() returning the first match is fine for the program. That being said, maybe i should consider checking the List for duplicates when filling it to raise an error when somehow two ressources with identical names do actually exist in different subdirectories. \$\endgroup\$ Commented Apr 12, 2019 at 9:49
  • \$\begingroup\$ What if files are added to a folder after the initial read? Is that something you need to support? \$\endgroup\$ Commented Apr 13, 2019 at 11:57

1 Answer 1

4
\$\begingroup\$

I don't think I would make this class static, unless it's going to be used extensively throughout the program. If not you can build up a large list of files, that may hang around in memory for no use, unless you remember to call ClearFiles(). Instead you could make a static method that could return an initialized object like:

public static FileManager Create(string directoryPath)
{
 FileManager fm = new FileManager();
 fm.AddFiles(directoryPath);
 return fm;
}

If you have a need for it, then make this instance as static somewhere in the application.


public static string GetPath(string filename)
{
 var File = Files.Find(x => x.Name == filename);
 return File.Path;
}

It returns only a first match of possible more matches, which will be in a directory high in the hierarchy, but what if you actually seek a path to a file in a subdirectory?

I think I would return a list/array/IEnumerable instead and let the client filter as needed.

Besides that, file names are case insensitive, so you should do:

Files.Find(x => string.Equals(x.Name, filename, StringComparison.CurrentCultureIgnoreCase));

public static void AddFiles(string directory)
{
 foreach (string file in Directory.GetFiles(directory))
 {
 Files.Add(new File() { Name = Path.GetFileName(file), Path = directory });
 }
 foreach (string subdirectory in Directory.GetDirectories(directory))
 {
 AddFiles(subdirectory);
 }
}

Nice recursive method. As an alternative you could consider to use DirectoryInfo instead - it can handle the recursive search for you:

 DirectoryInfo directory = new DirectoryInfo(directoryPath);
 Files.AddRange(
 directory
 .GetFiles("*.*", SearchOption.AllDirectories)
 .Select(fi => new File { Name = fi.Name, Path = fi.DirectoryName }));

There is no way to iterate through all the found File objects because the Files static member is private. I would consider to provide a public IEnumerable of some kind.


All in all, my implementation would look something like:

 public struct File
 {
 public string Name;
 public string Path;
 public override string ToString()
 {
 return $"{Name} => {Path}";
 }
 }
 public class FileManager : IEnumerable<File>
 {
 private List<File> Files = new List<File>();
 public void AddFiles(string directoryPath)
 {
 DirectoryInfo directory = new DirectoryInfo(directoryPath);
 Files.AddRange(
 directory
 .GetFiles("*.*", SearchOption.AllDirectories)
 .Select(fi => new File { Name = fi.Name, Path = fi.DirectoryName }));
 }
 public IEnumerable<string> GetPaths(string filename)
 {
 return Files
 .Where(x => string.Equals(x.Name, filename, StringComparison.CurrentCultureIgnoreCase))
 .Select(f => f.Path);
 }
 public void Clear()
 {
 Files.Clear();
 }
 public IEnumerator<File> GetEnumerator()
 {
 return Files.GetEnumerator();
 }
 IEnumerator IEnumerable.GetEnumerator()
 {
 return GetEnumerator();
 }
 public static FileManager Create(string directoryPath)
 {
 FileManager fm = new FileManager();
 fm.AddFiles(directoryPath);
 return fm;
 }
 }
answered Apr 12, 2019 at 15:58
\$\endgroup\$
2
  • \$\begingroup\$ Windows 10 supports marking directories/entire file systems as case-insensitive. It's not mentioned, but if this were dotnet core, then running on Linux/Mac would also support that. That said, if you're going to ignore case, I believe the proper thing to do for windows files (assuming case insensitivity) is to use OrdinalIgnoreCase \$\endgroup\$ Commented Apr 13, 2019 at 11:56
  • \$\begingroup\$ Too late to edit my comment, but that first line was supposed to read: Windows10 supports... case sensitive \$\endgroup\$ Commented Apr 13, 2019 at 13:20

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.