Skip to content

Commit 03de92d

Browse files
authored
Add documentation for CA1877: Collapse consecutive Path.Combine/Path.Join operations (#49638)
1 parent 3768151 commit 03de92d

File tree

6 files changed

+151
-0
lines changed

6 files changed

+151
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
title: "CA1877: Use 'Path.Combine' or 'Path.Join' overloads (code analysis)"
3+
description: "Learn about code analysis rule CA1877: Use 'Path.Combine' or 'Path.Join' overloads"
4+
ms.date: 11/05/2025
5+
ms.topic: reference
6+
f1_keywords:
7+
- CA1877
8+
- UsePathCombineOrPathJoinAnalyzer
9+
helpviewer_keywords:
10+
- CA1877
11+
dev_langs:
12+
- CSharp
13+
- VB
14+
ai-usage: ai-generated
15+
---
16+
17+
# CA1877: Use 'Path.Combine' or 'Path.Join' overloads
18+
19+
| Property | Value |
20+
|-------------------------------------|----------------------------------------|
21+
| **Rule ID** | CA1877 |
22+
| **Title** | Use `Path.Combine` or `Path.Join` overloads |
23+
| **Category** | [Performance](performance-warnings.md) |
24+
| **Fix is breaking or non-breaking** | Non-breaking |
25+
| **Enabled by default in .NET 10** | As suggestion |
26+
27+
## Cause
28+
29+
Multiple consecutive <xref:System.IO.Path.Combine%2A?displayProperty=nameWithType> or <xref:System.IO.Path.Join%2A?displayProperty=nameWithType> operations are used to build a path.
30+
31+
## Rule description
32+
33+
When you use multiple consecutive `Path.Combine` or `Path.Join` operations, it's more efficient to use an overload that accepts multiple path segments. This approach reduces the number of allocations and function calls, improving performance. Both methods provide overloads that accept multiple parameters, allowing you to collapse consecutive operations into a single call.
34+
35+
## How to fix violations
36+
37+
Replace consecutive `Path.Combine` or `Path.Join` operations with a single call using an overload that accepts all path segments.
38+
39+
A *code fix* that automatically performs this transformation is available.
40+
41+
## Example
42+
43+
The following code snippet shows a violation of CA1877:
44+
45+
:::code language="csharp" source="./snippets/csharp/all-rules/CA1877.cs" id="Violation":::
46+
:::code language="vb" source="./snippets/vb/all-rules/CA1877.vb" id="Violation":::
47+
48+
The following code snippet fixes the violation:
49+
50+
:::code language="csharp" source="./snippets/csharp/all-rules/CA1877.cs" id="Fix":::
51+
:::code language="vb" source="./snippets/vb/all-rules/CA1877.vb" id="Fix":::
52+
53+
## When to suppress warnings
54+
55+
It's safe to suppress a warning from this rule if performance isn't a concern.
56+
57+
## Suppress a warning
58+
59+
If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.
60+
61+
```csharp
62+
#pragma warning disable CA1877
63+
// The code that's violating the rule is on this line.
64+
#pragma warning restore CA1877
65+
```
66+
67+
To disable the rule for a file, folder, or project, set its severity to `none` in the [configuration file](../configuration-files.md).
68+
69+
```ini
70+
[*.{cs,vb}]
71+
dotnet_diagnostic.CA1877.severity = none
72+
```
73+
74+
For more information, see [How to suppress code analysis warnings](../suppress-warnings.md).
75+
76+
## See also
77+
78+
- [Performance rules](performance-warnings.md)
79+
- <xref:System.IO.Path.Combine%2A?displayProperty=nameWithType>
80+
- <xref:System.IO.Path.Join%2A?displayProperty=nameWithType>

docs/fundamentals/code-analysis/quality-rules/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ The following table lists code quality analysis rules.
176176
> | [CA1873: Avoid potentially expensive logging](ca1873.md) | In many situations, logging is disabled or set to a log level that results in an unnecessary evaluation for logging arguments. |
177177
> | [CA1874: Use `Regex.IsMatch`](ca1874.md) | `Regex.IsMatch` is simpler and faster than `Regex.Match(...).Success`. |
178178
> | [CA1875: Use `Regex.Count`](ca1875.md) | `Regex.Count` is simpler and faster than `Regex.Matches(...).Count`. |
179+
> | [CA1877: Use 'Path.Combine' or 'Path.Join' overloads](ca1877.md) | When multiple `Path.Combine` or `Path.Join` operations are nested, they can be collapsed into a single operation for better performance and readability. |
179180
> | [CA2000: Dispose objects before losing scope](ca2000.md) | Because an exceptional event might occur that will prevent the finalizer of an object from running, the object should be explicitly disposed before all references to it are out of scope. |
180181
> | [CA2002: Do not lock on objects with weak identity](ca2002.md) |An object is said to have a weak identity when it can be directly accessed across application domain boundaries. A thread that tries to acquire a lock on an object that has a weak identity can be blocked by a second thread in a different application domain that has a lock on the same object. |
181182
> | [CA2007: Do not directly await a Task](ca2007.md) | An asynchronous method [awaits](../../../csharp/language-reference/operators/await.md) a <xref:System.Threading.Tasks.Task> directly. When an asynchronous method awaits a <xref:System.Threading.Tasks.Task> directly, continuation occurs in the same thread that created the task. This behavior can be costly in terms of performance and can result in a deadlock on the UI thread. Consider calling <xref:System.Threading.Tasks.Task.ConfigureAwait(System.Boolean)?displayProperty=nameWithType> to signal your intention for continuation. |

docs/fundamentals/code-analysis/quality-rules/performance-warnings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,4 @@ Performance rules support high-performance libraries and applications.
8383
| [CA1873: Avoid potentially expensive logging](ca1873.md) | When logging methods are called, their arguments are evaluated regardless of whether the logging level is enabled. This can result in expensive operations being executed even when the log message won't be written. For better performance, guard expensive logging calls with a check to <xref:Microsoft.Extensions.Logging.ILogger.IsEnabled%2A> or use the `LoggerMessage` pattern. |
8484
| [CA1874: Use 'Regex.IsMatch'](ca1874.md) | <xref:System.Text.RegularExpressions.Regex.IsMatch*?displayProperty=nameWithType> is simpler and faster than `Regex.Match(...).Success`. |
8585
| [CA1875: Use 'Regex.Count'](ca1875.md) | <xref:System.Text.RegularExpressions.Regex.Count*?displayProperty=nameWithType> is simpler and faster than `Regex.Matches(...).Count`. |
86+
| [CA1877: Use 'Path.Combine' or 'Path.Join' overloads](ca1877.md) | Multiple consecutive <xref:System.IO.Path.Combine%2A?displayProperty=nameWithType> or <xref:System.IO.Path.Join%2A?displayProperty=nameWithType> operations can be collapsed into a single call using overloads that accept multiple path segments. |
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.IO;
2+
3+
class ViolationExample
4+
{
5+
// <Violation>
6+
public string GetFilePath(string folder, string subfolder, string filename)
7+
{
8+
// Violation.
9+
string temp = Path.Combine(folder, subfolder);
10+
return Path.Combine(temp, filename);
11+
}
12+
13+
public string GetLogPath(string baseDir, string date, string category)
14+
{
15+
// Violation.
16+
return Path.Join(Path.Join(baseDir, date), category);
17+
}
18+
// </Violation>
19+
}
20+
21+
class FixExample
22+
{
23+
// <Fix>
24+
public string GetFilePath(string folder, string subfolder, string filename)
25+
{
26+
// No violation.
27+
return Path.Combine(folder, subfolder, filename);
28+
}
29+
30+
public string GetLogPath(string baseDir, string date, string category)
31+
{
32+
// No violation.
33+
return Path.Join(baseDir, date, category);
34+
}
35+
// </Fix>
36+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Imports System.IO
2+
3+
Class ViolationExample
4+
' <Violation>
5+
Public Function GetFilePath(folder As String, subfolder As String, filename As String) As String
6+
' Violation.
7+
Dim temp As String = Path.Combine(folder, subfolder)
8+
Return Path.Combine(temp, filename)
9+
End Function
10+
11+
Public Function GetLogPath(baseDir As String, [date] As String, category As String) As String
12+
' Violation.
13+
Return Path.Join(Path.Join(baseDir, [date]), category)
14+
End Function
15+
' </Violation>
16+
End Class
17+
18+
Class FixExample
19+
' <Fix>
20+
Public Function GetFilePath(folder As String, subfolder As String, filename As String) As String
21+
' No violation.
22+
Return Path.Combine(folder, subfolder, filename)
23+
End Function
24+
25+
Public Function GetLogPath(baseDir As String, [date] As String, category As String) As String
26+
' No violation.
27+
Return Path.Join(baseDir, [date], category)
28+
End Function
29+
' </Fix>
30+
End Class
31+

docs/navigate/tools-diagnostics/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3287,6 +3287,8 @@ items:
32873287
href: ../../fundamentals/code-analysis/quality-rules/ca1874.md
32883288
- name: CA1875
32893289
href: ../../fundamentals/code-analysis/quality-rules/ca1875.md
3290+
- name: CA1877
3291+
href: ../../fundamentals/code-analysis/quality-rules/ca1877.md
32903292
- name: SingleFile rules
32913293
items:
32923294
- name: Overview

0 commit comments

Comments
 (0)