Style
Suggestion (Info)
Suggests simplifying chained method calls within string interpolation expressions by extracting them to a variable or creating an extension method. When you have 2 or more chained method calls inside an interpolation expression, the code can become difficult to read and maintain.
- 2+ chained method calls: When an interpolation expression contains 2 or more chained method calls, a suggestion is raised
- Single method calls: No suggestion - single method calls are perfectly acceptable
- Property access: Property access after a method call (e.g.,
.Trim().Length) counts as only one method call - Applies to: All interpolated string types including regular (
$""), verbatim ($@""), and raw ($""" """) strings - Nested interpolations: Each interpolation is analyzed independently
Extracting chained method calls from interpolated strings:
- Improves code readability by separating data transformation from string formatting
- Makes debugging easier - you can inspect intermediate values
- Allows for better error handling and null checking
- Reduces cognitive load when reading the code
- Makes the code more maintainable
// non-compliant
var result = $"Hello {myVar.ToString().ToLower()} world";
// compliant - extract to a variable
var formattedValue = myVar.ToString().ToLower();
var result = $"Hello {formattedValue} world";
// compliant - create an extension method
var result = $"Hello {myVar.ToFormattedString()} world";// non-compliant
var result = $"Value: {myVar.ToString().Trim().ToLower()}";
// compliant - extract to a variable
var formattedValue = myVar
.ToString()
.Trim()
.ToLower();
var result = $"Value: {formattedValue}";// non-compliant
var result = $"{firstName.Trim().ToUpper()} {lastName.Trim().ToUpper()}";
// compliant - extract both to variables
var first = firstName.Trim().ToUpper();
var last = lastName.Trim().ToUpper();
var result = $"{first} {last}";// compliant - single method call is fine
var result = $"Hello {myVar.ToString()} world";
var result2 = $"Length: {text.Trim().Length}"; // Property access doesn't count// compliant - property access only
var result = $"Length is {myVar.Length}";// compliant - just a variable
var result = $"Value is {myVar}";// non-compliant
var result = $"First item: {items.First().ToUpper()}";
// compliant
var firstItem = items.First().ToUpper();
var result = $"First item: {firstItem}";// non-compliant
var path = $@"C:\Users\{name.Trim().ToLower()}\Documents";
// compliant
var userName = name.Trim().ToLower();
var path = $@"C:\Users\{userName}\Documents";// non-compliant
var json = $"""
{
"name": "{data.GetName().ToLower()}"
}
""";
// compliant
var formattedName = data.GetName().ToLower();
var json = $"""
{
"name": "{formattedName}"
}
""";There are two recommended approaches to fix this suggestion:
Extract the chained method calls to a local variable before the interpolated string:
// Before
var message = $"Hello {user.GetName().Trim().ToLower()}!";
// After
var formattedName = user.GetName().Trim().ToLower();
var message = $"Hello {formattedName}!";If you frequently use the same chain of method calls, consider creating an extension method:
// Extension method
public static class StringExtensions
{
public static string ToFormattedName(this string name)
=> name.Trim().ToLower();
}
// Usage
var message = $"Hello {user.GetName().ToFormattedName()}!";This rule automatically skips analysis of generated code. Generated code is identified by:
- GeneratedCode Attribute: Classes or types marked with
[GeneratedCode]attribute fromSystem.CodeDom.Compiler - Auto-generated Headers: Files containing "auto-generated" in header comments (case-insensitive)
- ATC203: Method chains with 2 or more calls should be placed on separate lines - Method chain formatting (excludes interpolated strings)
- ATC201: Single parameter should be kept inline when declaration is short - Parameter formatting
- ATC202: Multi parameters should be separated on individual lines - Parameter formatting