From 2cce925c810336d81d7d65f8cca16a84bfc55288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 12 Mar 2026 16:08:01 +0100 Subject: [PATCH] Avoid List allocation in Assert.ContainsSingle Replace .Where(predicate).ToList() with a manual foreach loop that counts matches and captures the first one. This avoids allocating a List on every call. The non-generic overload already used this pattern. --- .../Assertions/Assert.Contains.cs | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs b/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs index 819452d1f4..19fb68e0a6 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Contains.cs @@ -154,23 +154,38 @@ public static T ContainsSingle(IEnumerable collection, string? message = " /// The item that matches the predicate. public static T ContainsSingle(Func predicate, IEnumerable collection, string? message = "", [CallerArgumentExpression(nameof(predicate))] string predicateExpression = "", [CallerArgumentExpression(nameof(collection))] string collectionExpression = "") { - var matchingElements = collection.Where(predicate).ToList(); - int actualCount = matchingElements.Count; + T firstMatch = default!; + int matchCount = 0; - if (actualCount == 1) + foreach (T item in collection) { - return matchingElements[0]; + if (!predicate(item)) + { + continue; + } + + if (matchCount == 0) + { + firstMatch = item; + } + + matchCount++; + } + + if (matchCount == 1) + { + return firstMatch; } if (string.IsNullOrEmpty(predicateExpression)) { string userMessage = BuildUserMessageForCollectionExpression(message, collectionExpression); - ThrowAssertContainsSingleFailed(actualCount, userMessage); + ThrowAssertContainsSingleFailed(matchCount, userMessage); } else { string userMessage = BuildUserMessageForPredicateExpressionAndCollectionExpression(message, predicateExpression, collectionExpression); - ThrowAssertSingleMatchFailed(actualCount, userMessage); + ThrowAssertSingleMatchFailed(matchCount, userMessage); } // Unreachable code but compiler cannot work it out