Skip to content

Latest commit

 

History

History
182 lines (130 loc) · 4.8 KB

File metadata and controls

182 lines (130 loc) · 4.8 KB

ATC204: Chained method calls in interpolated strings should be simplified

Category

Style

Severity

Suggestion (Info)

Description

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.

Rules

  1. 2+ chained method calls: When an interpolation expression contains 2 or more chained method calls, a suggestion is raised
  2. Single method calls: No suggestion - single method calls are perfectly acceptable
  3. Property access: Property access after a method call (e.g., .Trim().Length) counts as only one method call
  4. Applies to: All interpolated string types including regular ($""), verbatim ($@""), and raw ($""" """​) strings
  5. Nested interpolations: Each interpolation is analyzed independently

Motivation

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

Examples

Two chained method calls

// 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";

Three or more chained method calls

// non-compliant
var result = $"Value: {myVar.ToString().Trim().ToLower()}";

// compliant - extract to a variable
var formattedValue = myVar
    .ToString()
    .Trim()
    .ToLower();
var result = $"Value: {formattedValue}";

Multiple interpolations with chains

// 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}";

Single method call (allowed)

// compliant - single method call is fine
var result = $"Hello {myVar.ToString()} world";
var result2 = $"Length: {text.Trim().Length}"; // Property access doesn't count

Property access (allowed)

// compliant - property access only
var result = $"Length is {myVar.Length}";

Variable access (allowed)

// compliant - just a variable
var result = $"Value is {myVar}";

LINQ in interpolation

// non-compliant
var result = $"First item: {items.First().ToUpper()}";

// compliant
var firstItem = items.First().ToUpper();
var result = $"First item: {firstItem}";

Verbatim interpolated strings

// non-compliant
var path = $@"C:\Users\{name.Trim().ToLower()}\Documents";

// compliant
var userName = name.Trim().ToLower();
var path = $@"C:\Users\{userName}\Documents";

Raw interpolated strings (C# 11+)

// non-compliant
var json = $"""
    {
        "name": "{data.GetName().ToLower()}"
    }
    """;

// compliant
var formattedName = data.GetName().ToLower();
var json = $"""
    {
        "name": "{formattedName}"
    }
    """;

How to Fix

There are two recommended approaches to fix this suggestion:

1. Extract to a variable

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}!";

2. Create an extension method

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()}!";

Generated Code

This rule automatically skips analysis of generated code. Generated code is identified by:

  1. GeneratedCode Attribute: Classes or types marked with [GeneratedCode] attribute from System.CodeDom.Compiler
  2. Auto-generated Headers: Files containing "auto-generated" in header comments (case-insensitive)

Related Rules