dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.91k stars 4.63k forks source link

Path.Join with four arguments #27407

Closed jnm2 closed 4 years ago

jnm2 commented 5 years ago

I wanted to write this:

Path.Join(assetsDirectory, "objects", hash.AsSpan().Slice(0, 2), hash)

This is the workaround:

Path.Join(assetsDirectory, "objects", Path.Join(hash.AsSpan().Slice(0, 2), hash))

Path.Combine goes up to four; why is this API different?

API Summary

We didn't propose a 4 method overload originally as TryJoin allowed writing low-allocating versions for 4+ paths.

As we already have the same helper code for both Combine/Join, there is no reason not to expose the 4 method overloads for Join. TryJoin isn't as straight-forward and can be composed with multiple calls without extra allocation cost- as such we'll leave at 3 for now.

API Signature

namespace System.IO
{
    public static class Path
    {
        public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, ReadOnlySpan<char> path4);
        public static string Join(string path1, string path2, string path3, string path4);
    }
}
danmoseley commented 5 years ago

@JeremyKuhne any objection? It would obviously be easy to add

JeremyKuhne commented 5 years ago

It's simply a matter of diminishing returns vs. additional complexity. The idea was that for 4+ you could compose yourself using TryJoin if allocation was critical. I'm fine with adding this one as we already have the helper.

https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/shared/System/IO/Path.cs#L607

jbhensley commented 5 years ago

If I'm looking at this correct, there will be two PRs. One in corefx for the API and one in coreclr for the actual implementation?

I assume we'll want to add overloads for TryJoin and Join(string...) to maintain parity. How about this:

public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, ReadOnlySpan<char> path4) { throw null; }
public static bool TryJoin(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, ReadOnlySpan<char> path4, Span<char> destination, out int charsWritten) { throw null; }
public static string Join(string path1, string path2, string path3, string path4) { throw null; }
JeremyKuhne commented 5 years ago

@jbhensley Good point on the string one. I'd rather not do the TryJoin unless we really demonstrate a demand for it. It isn't as straight-forward as it might seem at first as it has to not copy data if it can't fit everything.

jbhensley commented 5 years ago

Totally your call. Looking at the code for TryJoin with three, it doesn't look like it would be too difficult to extend out to four. Don't know if there is any demand, but things feel a bit "off" if Join goes to four and TryJoin goes to three. Just my personal take.

JeremyKuhne commented 5 years ago

It isn't horrible to implement for sure, but I'm trying to be a little cautious by default. We'll see what the API review has to say about it- if the general consensus is to match I'm fine with it.

jbhensley commented 5 years ago

I'd like to take this on if the API gets approved in whichever form. Looks pretty straightforward.

JeremyKuhne commented 5 years ago

@jbhensley sure! @karelz, can you get him set up so we can assign the issue to him?

karelz commented 5 years ago

@jbhensley invite sent. Ping us once you accept it - we can then assign it to you. It will auto-subscribe you to all repo notifications (500+ per day). We recommend to switch repo to Not-Watching which will reduce notifications to just explicit subscriptions, mentions and assignments.

jbhensley commented 5 years ago

@karelz Invite accepted

jbhensley commented 5 years ago

@karelz @JeremyKuhne Let me know if there is anything else you need me to do. I'm sure this is not a very high priority item.

karelz commented 5 years ago

Sorry, I was at MSIgnite, not on email/GitHub. @JeremyKuhne can you push on the API review next Tuesday please?

JeremyKuhne commented 5 years ago

can you push on the API review next Tuesday please?

As soon as we get back to the backlog I'll be online for it.

terrajobst commented 5 years ago

This makes sense to me. We'd also match the pattern of Path.Combine() which has specialized overloads for up to four arguments as well (and then falls back to params).

terrajobst commented 5 years ago

Looks good.

namespace System.IO
{
    public static class Path
    {
        public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, ReadOnlySpan<char> path4);
        public static string Join(string path1, string path2, string path3, string path4);

        // For consistency we should also add this one:
        public static string Join(params string[] paths);
    }
}

@JeremyKuhne Any objections to adding the params one too? We want people to move from Path.Combine() to this one and passing more than four paths seems reasonable. For spans we're out of luck for know, though.

JeremyKuhne commented 5 years ago

Any objections to adding the params one too?

Not really.

Anipik commented 5 years ago

@jbhensley can you are planning to take this up ?

Anipik commented 5 years ago

Tests and api are both merged.