Using operator overloads for concatenating file system paths in CSharp

The past few days, I’ve been working on some cross-platform C# code. In this code, I needed to build a path to a file, by concatenating folder names. As you may know the path separator on Windows and Linux operating systems are different: one has a backward slash (\), the other has a forward slash (/).

Luckily for me, the .NET framework(s) contain a utility function for this: Path.Combine handles this for me! Here’s an example:

var rootPath = ...;
var filePath = Path.Combine(rootPath, "subfolder", "another", "file.txt");

This will generate a platform-specific path:

  • On Windows: “…\subfolder\another\file.txt”
  • On Linux: “…/subfolder/another/file.txt”

Great! However, I found something else in the codebase I was working on (ReSharper):

var rootPath = ...;
var filePath = rootPath / "subfolder" / "another" / "file.txt";

Whoa! This almost looks like path separators! And the great thing is that this also returns a platform-specific path.

After looking at the code a bit, I realized this was just clever use of operator overloading in C#. Some code to achieve the same result as the above:

var rootPath = new FilePath(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
var path = rootPath / ".nuget" / "packages";

public struct FilePath
{
    public string Path { get; }

    public FilePath(string path)
    {
        Path = path ?? throw new ArgumentNullException(nameof(path));
    }
        
    public static FilePath operator /(FilePath left, FilePath right)
    {
        return new FilePath(System.IO.Path.Combine(left.Path, right.Path));
    }

    public static FilePath operator /(FilePath left, string right)
    {
        return new FilePath(System.IO.Path.Combine(left.Path, right));
    }

    public override string ToString()
    {
        return Path;
    }
}

ReSharper’s internal use of operator overloads made me realize that I’ve not been using this enough in the past. It allows nice looking syntax using +, -, /, … - see full list.

This could be used on more technical things (I’d love for some operators overloads like + on an IEnumerable<> or a collection to create a union of two collections), but also on domain objects (a >> operator to move a Person to a new Address and other types of syntax abuse).

Enjoy!

Leave a Comment

avatar

9 responses

  1. Avatar for Matthias
    Matthias October 26th, 2017

    I've also had this implemented like here once. Now after some time I'm not so sure anymore about overloading the + operator. Is it obvious what this will yield?

    "output" / "package" + i / "sub"

  2. Avatar for Maarten Balliauw
    Maarten Balliauw October 26th, 2017

    Yeah it's a bit strange in that case.

  3. Avatar for James Curran
    James Curran October 26th, 2017

    Now, I'm going to assume that i is an integer, and you are going for "\output\package5\sub".
    However, + and / should maintain their arithmatic precedence, which means it would try to perfrom the int-divided-by-string, before the string-plus-int. That probably won't compile.

    However, "output" / ("package" + i) / "sub" should handle any confusion of either the reader or the compiler.

  4. Avatar for James Curran
    James Curran October 26th, 2017

    Also, I think we need to add:
    public static implicit operator FilePath (Environment.SpecialFolder folder)
    {
    return new FilePath(Environment.GetFolderPath(folder));
    }

    to FilePath. That will let us reduce our example down to :

    var path = (FilePath) Environment.SpecialFolder.UserProfile / (".nuget" + i) / "packages";

  5. Avatar for Maarten Balliauw
    Maarten Balliauw October 27th, 2017

    Great hint! Thanks, James!

  6. Avatar for Lorenz Cuno Klopfenstein
    Lorenz Cuno Klopfenstein October 27th, 2017

    Very neat idea!

  7. Avatar for Dan Miser
    Dan Miser October 31st, 2017

    Nice article. I was going to add operator overloading for StringBuilder to make things look more compact when Appending, but then I came across Eric Lippert talking about why that wasn't a good idea. From a theory point of view, I agree with him. From a practical one, I still think there is some value to that (and this).

    https://stackoverflow.com/q...

  8. Avatar for George Chakhidze
    George Chakhidze November 3rd, 2017

    And also this 😃


    public static implicit operator FilePath(string path)
    {
    return new FilePath(path);
    }

    and then:


    FilePath root = Environment.CurrentDirectory;
    FilePath path = root / "Hello" / "World";
  9. Avatar for Lazy Heap
    Lazy Heap July 6th, 2018

    That's nice idea, but I think wee need to overload "\" also if we are talking about backward slash as separator for those who are familiar with backward slash in paths.