Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions docs/Rules/MA0090.md
Original file line number Diff line number Diff line change
@@ -1 +1,65 @@
# MA0090 - Remove empty else/finally block

This rule detects empty `else` and `finally` blocks that contain no executable code and suggests removing them to improve code readability and reduce clutter. Empty blocks serve no functional purpose and can make code harder to read by adding unnecessary visual noise.

````csharp
// non-compliant
if (condition)
{
DoSomething();
}
else
{
}

// compliant
if (condition)
{
DoSomething();
}

// non-compliant
try
{
DoSomething();
}
catch (Exception ex)
{
HandleException(ex);
}
finally
{
}

// compliant
try
{
DoSomething();
}
catch (Exception ex)
{
HandleException(ex);
}

// compliant - blocks with comments are not flagged
if (condition)
{
DoSomething();
}
else
{
// TODO: Implement this feature later
}

// compliant - blocks with preprocessor directives are not flagged
try
{
DoSomething();
}
finally
{
#if DEBUG
Console.WriteLine("Debug mode");
#endif
}
````
85 changes: 85 additions & 0 deletions docs/Rules/MA0098.md
Original file line number Diff line number Diff line change
@@ -1 +1,86 @@
# MA0098 - Use indexer instead of LINQ methods

When working with collections that support indexing (such as arrays, `List<T>`, `IList<T>`, or `IReadOnlyList<T>`), using the indexer syntax `[index]` is more efficient than LINQ methods like `ElementAt()`, `First()`, and `Last()`. This rule suggests replacing these LINQ methods with direct indexer access for better performance.

The indexer approach provides direct access to elements without the overhead of LINQ extension methods and enumeration, resulting in faster execution and reduced memory allocations.

## Examples

````csharp
var list = new List<int>();

_ = list.ElementAt(5); // non-compliant
_ = list[5]; // compliant

_ = list.First(); // non-compliant
_ = list[0]; // compliant

_ = list.Last(); // non-compliant
_ = list[^1]; // compliant
_ = list[list.Count - 1]; // compliant

_ = list.FirstOrDefault(); // compliant
_ = list.First(x => x > 1); // compliant
````

## C# 8+ Index and Range Support

Starting with C# 8.0 and when targeting frameworks that support the Index and Range feature (.NET Core 3.0+, .NET 5+), the analyzer can suggest using the hat operator (`^`) for accessing elements from the end:

````csharp
// C# 8+ with compatible target framework
var array = new int[10];

// The analyzer suggests using the hat operator for Last()
var last = array[^1]; // ✅ Equivalent to array[array.Length - 1]

// For older language versions or target frameworks, it uses the traditional syntax
var last = array[array.Length - 1]; // ✅ Fallback for older versions
````

## Performance Benefits

Using indexer access instead of LINQ methods provides several performance advantages:

- **Direct Access**: Indexers provide O(1) direct access to elements, while LINQ methods may involve enumeration overhead
- **No Extension Method Overhead**: Eliminates the cost of extension method calls and delegate invocations
- **Reduced Memory Allocations**: Avoids temporary objects and iterator allocations that LINQ methods might create
- **Better JIT Optimization**: The JIT compiler can better optimize simple indexer access compared to generic extension methods

## When This Rule Doesn't Apply

This rule has specific limitations and won't trigger in certain scenarios:

### Methods with Predicates

The rule only applies to parameterless versions of `First()` and `Last()`, and single-parameter `ElementAt()`. It doesn't apply when using predicates:

````csharp
// These are NOT flagged by the rule - predicates are necessary
var firstEven = list.First(x => x % 2 == 0);
var lastNegative = array.Last(x => x < 0);
````

### OrDefault Variants

The rule doesn't apply to the "OrDefault" variants of these methods:

````csharp
// These are NOT flagged by the rule
var item1 = list.FirstOrDefault(); // ✅ Rule doesn't apply
var item2 = list.LastOrDefault(); // ✅ Rule doesn't apply
var item3 = list.ElementAtOrDefault(5); // ✅ Rule doesn't apply
````

### Unsupported Collection Types

The rule only applies to collections that implement `IList<T>` or `IReadOnlyList<T>`:

````csharp
// These are NOT flagged by the rule - no indexer support
IEnumerable<int> enumerable = GetNumbers();
var first = enumerable.First(); // ✅ Rule doesn't apply

HashSet<int> hashSet = new();
var firstFromSet = hashSet.First(); // ✅ Rule doesn't apply
````