diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
index 52661da90e..d8b1907018 100644
--- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
+++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
@@ -14,6 +14,7 @@ CA1844 | Performance | Info | ProvideStreamMemoryBasedAsyncOverrides, [Documenta
CA1845 | Performance | Info | UseSpanBasedStringConcat, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1845)
CA1846 | Performance | Info | PreferAsSpanOverSubstring, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1846)
CA2250 | Usage | Info | UseCancellationTokenThrowIfCancellationRequested, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250)
+CA2251 | Usage | Hidden | UseStringEqualsOverStringCompare, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2251)
### Removed Rules
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
index 01e3b55deb..9c032952eb 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
@@ -1635,4 +1635,16 @@
Replace 'WhenAll' call with argument
+
+ Use 'string.Equals'
+
+
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+
+
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+
+
+ Use 'string.Equals'
+
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseStringEqualsOverStringCompare.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseStringEqualsOverStringCompare.Fixer.cs
new file mode 100644
index 0000000000..df579cd385
--- /dev/null
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseStringEqualsOverStringCompare.Fixer.cs
@@ -0,0 +1,215 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Analyzer.Utilities;
+using Analyzer.Utilities.Extensions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Operations;
+
+using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources;
+using RequiredSymbols = Microsoft.NetCore.Analyzers.Runtime.UseStringEqualsOverStringCompare.RequiredSymbols;
+
+namespace Microsoft.NetCore.Analyzers.Runtime
+{
+ [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared]
+ public sealed class UseStringEqualsOverStringCompareFixer : CodeFixProvider
+ {
+ public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(UseStringEqualsOverStringCompare.RuleId);
+
+ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ var document = context.Document;
+ var token = context.CancellationToken;
+ var semanticModel = await document.GetSemanticModelAsync(token).ConfigureAwait(false);
+
+ _ = RequiredSymbols.TryGetSymbols(semanticModel.Compilation, out var symbols);
+ RoslynDebug.Assert(symbols is not null);
+
+ var root = await document.GetSyntaxRootAsync(token).ConfigureAwait(false);
+ var node = root.FindNode(context.Span, getInnermostNodeForTie: true);
+ if (semanticModel.GetOperation(node, token) is not IBinaryOperation violation)
+ return;
+
+ // Get the replacer that applies to the reported violation.
+ var replacer = GetOperationReplacers(symbols).First(x => x.IsMatch(violation));
+
+ var codeAction = CodeAction.Create(
+ Resx.UseStringEqualsOverStringCompareCodeFixTitle,
+ CreateChangedDocument,
+ nameof(Resx.UseStringEqualsOverStringCompareCodeFixTitle));
+ context.RegisterCodeFix(codeAction, context.Diagnostics);
+ return;
+
+ // Local functions
+
+ async Task CreateChangedDocument(CancellationToken token)
+ {
+ var editor = await DocumentEditor.CreateAsync(document, token).ConfigureAwait(false);
+ var replacementNode = replacer.CreateReplacementExpression(violation, editor.Generator);
+ editor.ReplaceNode(violation.Syntax, replacementNode);
+
+ return editor.GetChangedDocument();
+ }
+ }
+
+ public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
+
+ private static ImmutableArray GetOperationReplacers(RequiredSymbols symbols)
+ {
+ return ImmutableArray.Create(
+ new StringStringCaseReplacer(symbols),
+ new StringStringBoolReplacer(symbols),
+ new StringStringStringComparisonReplacer(symbols));
+ }
+
+ ///
+ /// Base class for an object that generate the replacement code for a reported violation.
+ ///
+ private abstract class OperationReplacer
+ {
+ protected OperationReplacer(RequiredSymbols symbols)
+ {
+ Symbols = symbols;
+ }
+
+ protected RequiredSymbols Symbols { get; }
+
+ ///
+ /// Indicates whether the current applies to the specified violation.
+ ///
+ /// The at the location reported by the analyzer.
+ /// True if the current applies to the specified violation.
+ public abstract bool IsMatch(IBinaryOperation violation);
+
+ ///
+ /// Creates a replacement node for a violation that the current applies to.
+ /// Asserts if the current does not apply to the specified violation.
+ ///
+ /// The obtained at the location reported by the analyzer.
+ /// must return for this operation.
+ ///
+ ///
+ public abstract SyntaxNode CreateReplacementExpression(IBinaryOperation violation, SyntaxGenerator generator);
+
+ protected SyntaxNode CreateEqualsMemberAccess(SyntaxGenerator generator)
+ {
+ var stringTypeExpression = generator.TypeExpressionForStaticMemberAccess(Symbols.StringType);
+ return generator.MemberAccessExpression(stringTypeExpression, nameof(string.Equals));
+ }
+
+ protected static IInvocationOperation GetInvocation(IBinaryOperation violation)
+ {
+ var result = UseStringEqualsOverStringCompare.GetInvocationFromEqualityCheckWithLiteralZero(violation);
+
+ RoslynDebug.Assert(result is not null);
+
+ return result;
+ }
+
+ protected static SyntaxNode InvertIfNotEquals(SyntaxNode stringEqualsInvocationExpression, IBinaryOperation equalsOrNotEqualsOperation, SyntaxGenerator generator)
+ {
+ return equalsOrNotEqualsOperation.OperatorKind is BinaryOperatorKind.NotEquals ?
+ generator.LogicalNotExpression(stringEqualsInvocationExpression) :
+ stringEqualsInvocationExpression;
+ }
+ }
+
+ ///
+ /// Replaces violations.
+ ///
+ private sealed class StringStringCaseReplacer : OperationReplacer
+ {
+ public StringStringCaseReplacer(RequiredSymbols symbols)
+ : base(symbols)
+ { }
+
+ public override bool IsMatch(IBinaryOperation violation) => UseStringEqualsOverStringCompare.IsStringStringCase(violation, Symbols);
+
+ public override SyntaxNode CreateReplacementExpression(IBinaryOperation violation, SyntaxGenerator generator)
+ {
+ RoslynDebug.Assert(IsMatch(violation));
+
+ var compareInvocation = GetInvocation(violation);
+ var equalsInvocationSyntax = generator.InvocationExpression(
+ CreateEqualsMemberAccess(generator),
+ compareInvocation.Arguments.GetArgumentsInParameterOrder().Select(x => x.Value.Syntax));
+
+ return InvertIfNotEquals(equalsInvocationSyntax, violation, generator);
+ }
+ }
+
+ ///
+ /// Replaces violations.
+ ///
+ private sealed class StringStringBoolReplacer : OperationReplacer
+ {
+ public StringStringBoolReplacer(RequiredSymbols symbols)
+ : base(symbols)
+ { }
+
+ public override bool IsMatch(IBinaryOperation violation) => UseStringEqualsOverStringCompare.IsStringStringBoolCase(violation, Symbols);
+
+ public override SyntaxNode CreateReplacementExpression(IBinaryOperation violation, SyntaxGenerator generator)
+ {
+ RoslynDebug.Assert(IsMatch(violation));
+
+ var compareInvocation = GetInvocation(violation);
+
+ // We know that the 'ignoreCase' argument in 'string.Compare(string, string, bool)' is a boolean literal
+ // because we've asserted that 'IsMatch' returns true.
+ var ignoreCaseLiteral = (ILiteralOperation)compareInvocation.Arguments.GetArgumentForParameterAtIndex(2).Value;
+
+ // If the violation contains a call to 'string.Compare(x, y, true)' then we
+ // replace it with a call to 'string.Equals(x, y, StringComparison.CurrentCultureIgnoreCase)'.
+ // If the violation contains a call to 'string.Compare(x, y, false)' then we
+ // replace it with a call to 'string.Equals(x, y, StringComparison.CurrentCulture)'.
+ var stringComparisonEnumMemberName = (bool)ignoreCaseLiteral.ConstantValue.Value ?
+ nameof(StringComparison.CurrentCultureIgnoreCase) :
+ nameof(StringComparison.CurrentCulture);
+ var stringComparisonMemberAccessSyntax = generator.MemberAccessExpression(
+ generator.TypeExpressionForStaticMemberAccess(Symbols.StringComparisonType),
+ stringComparisonEnumMemberName);
+
+ var equalsInvocationSyntax = generator.InvocationExpression(
+ CreateEqualsMemberAccess(generator),
+ compareInvocation.Arguments.GetArgumentForParameterAtIndex(0).Value.Syntax,
+ compareInvocation.Arguments.GetArgumentForParameterAtIndex(1).Value.Syntax,
+ stringComparisonMemberAccessSyntax);
+
+ return InvertIfNotEquals(equalsInvocationSyntax, violation, generator);
+ }
+ }
+
+ ///
+ /// Replaces violations.
+ ///
+ private sealed class StringStringStringComparisonReplacer : OperationReplacer
+ {
+ public StringStringStringComparisonReplacer(RequiredSymbols symbols)
+ : base(symbols)
+ { }
+
+ public override bool IsMatch(IBinaryOperation violation) => UseStringEqualsOverStringCompare.IsStringStringStringComparisonCase(violation, Symbols);
+
+ public override SyntaxNode CreateReplacementExpression(IBinaryOperation violation, SyntaxGenerator generator)
+ {
+ RoslynDebug.Assert(IsMatch(violation));
+
+ var invocation = GetInvocation(violation);
+ var equalsInvocationSyntax = generator.InvocationExpression(
+ CreateEqualsMemberAccess(generator),
+ invocation.Arguments.GetArgumentsInParameterOrder().Select(x => x.Value.Syntax));
+
+ return InvertIfNotEquals(equalsInvocationSyntax, violation, generator);
+ }
+ }
+ }
+}
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseStringEqualsOverStringCompare.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseStringEqualsOverStringCompare.cs
new file mode 100644
index 0000000000..98b59abdfa
--- /dev/null
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseStringEqualsOverStringCompare.cs
@@ -0,0 +1,276 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Analyzer.Utilities;
+using Analyzer.Utilities.Extensions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources;
+
+namespace Microsoft.NetCore.Analyzers.Runtime
+{
+ ///
+ /// Reports a diagnostic on any that:
+ ///
+ /// Is an equals or not-equals operation
+ /// One operand is a literal zero
+ /// The other operand is an of an eligible
+ /// string.Compare overload.
+ ///
+ /// See all the Is...Case methods to see the string.Compare overloads that are supported.
+ ///
+ [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
+ public sealed class UseStringEqualsOverStringCompare : DiagnosticAnalyzer
+ {
+ internal const string RuleId = "CA2251";
+
+ private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(Resx.UseStringEqualsOverStringCompareTitle), Resx.ResourceManager, typeof(Resx));
+ private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(Resx.UseStringEqualsOverStringCompareMessage), Resx.ResourceManager, typeof(Resx));
+ private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(Resx.UseStringEqualsOverStringCompareDescription), Resx.ResourceManager, typeof(Resx));
+
+ internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
+ RuleId,
+ s_localizableTitle,
+ s_localizableMessage,
+ DiagnosticCategory.Usage,
+ RuleLevel.IdeHidden_BulkConfigurable,
+ s_localizableDescription,
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterCompilationStartAction(OnCompilationStart);
+ }
+
+ private static void OnCompilationStart(CompilationStartAnalysisContext context)
+ {
+ if (!RequiredSymbols.TryGetSymbols(context.Compilation, out var symbols))
+ return;
+ context.RegisterOperationAction(AnalyzeOperation, OperationKind.Binary);
+ return;
+
+ // Local functions
+
+ void AnalyzeOperation(OperationAnalysisContext context)
+ {
+ var operation = (IBinaryOperation)context.Operation;
+ foreach (var selector in CaseSelectors)
+ {
+ if (selector(operation, symbols))
+ {
+ context.ReportDiagnostic(operation.CreateDiagnostic(Rule));
+ return;
+ }
+ }
+ }
+ }
+
+ internal sealed class RequiredSymbols
+ {
+ private RequiredSymbols(
+ INamedTypeSymbol stringType,
+ INamedTypeSymbol boolType,
+ INamedTypeSymbol stringComparisonType,
+ IMethodSymbol? compareStringString,
+ IMethodSymbol? compareStringStringBool,
+ IMethodSymbol? compareStringStringStringComparison,
+ IMethodSymbol? equalsStringString,
+ IMethodSymbol? equalsStringStringStringComparison)
+ {
+ StringType = stringType;
+ BoolType = boolType;
+ StringComparisonType = stringComparisonType;
+ CompareStringString = compareStringString;
+ CompareStringStringBool = compareStringStringBool;
+ CompareStringStringStringComparison = compareStringStringStringComparison;
+ EqualsStringString = equalsStringString;
+ EqualsStringStringStringComparison = equalsStringStringStringComparison;
+ }
+
+ public static bool TryGetSymbols(Compilation compilation, [NotNullWhen(true)] out RequiredSymbols? symbols)
+ {
+ symbols = default;
+
+ var stringType = compilation.GetSpecialType(SpecialType.System_String);
+ var boolType = compilation.GetSpecialType(SpecialType.System_Boolean);
+
+ if (stringType is null || boolType is null)
+ return false;
+
+ if (!compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemStringComparison, out var stringComparisonType))
+ return false;
+
+ var compareMethods = stringType.GetMembers(nameof(string.Compare))
+ .OfType()
+ .Where(x => x.IsStatic);
+ var compareStringString = compareMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType);
+ var compareStringStringBool = compareMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType, boolType);
+ var compareStringStringStringComparison = compareMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType, stringComparisonType);
+
+ var equalsMethods = stringType.GetMembers(nameof(string.Equals))
+ .OfType()
+ .Where(x => x.IsStatic);
+ var equalsStringString = equalsMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType);
+ var equalsStringStringStringComparison = equalsMethods.GetFirstOrDefaultMemberWithParameterTypes(stringType, stringType, stringComparisonType);
+
+ // Bail if we do not have at least one complete pair of Compare-Equals methods in the compilation.
+ if ((compareStringString is null || equalsStringString is null) &&
+ (compareStringStringBool is null || equalsStringStringStringComparison is null) &&
+ (compareStringStringStringComparison is null || equalsStringStringStringComparison is null))
+ {
+ return false;
+ }
+
+ symbols = new RequiredSymbols(
+ stringType, boolType, stringComparisonType,
+ compareStringString, compareStringStringBool, compareStringStringStringComparison,
+ equalsStringString, equalsStringStringStringComparison);
+ return true;
+ }
+
+ public INamedTypeSymbol StringType { get; }
+ public INamedTypeSymbol BoolType { get; }
+ public INamedTypeSymbol StringComparisonType { get; }
+ public IMethodSymbol? CompareStringString { get; }
+ public IMethodSymbol? CompareStringStringBool { get; }
+ public IMethodSymbol? CompareStringStringStringComparison { get; }
+ public IMethodSymbol? EqualsStringString { get; }
+ public IMethodSymbol? EqualsStringStringStringComparison { get; }
+ }
+
+ ///
+ /// If the specified :
+ ///
+ /// Is an equals or not-equals operation
+ /// One operand is a literal zero
+ /// The other operand is any
+ ///
+ /// then this method returns the .
+ /// Otherwise, returns null.
+ ///
+ ///
+ ///
+ internal static IInvocationOperation? GetInvocationFromEqualityCheckWithLiteralZero(IBinaryOperation binaryOperation)
+ {
+ if (binaryOperation.OperatorKind is not (BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals))
+ return default;
+
+ if (IsLiteralZero(binaryOperation.LeftOperand))
+ return binaryOperation.RightOperand as IInvocationOperation;
+ else if (IsLiteralZero(binaryOperation.RightOperand))
+ return binaryOperation.LeftOperand as IInvocationOperation;
+ else
+ return default;
+
+ // Local functions
+
+ static bool IsLiteralZero(IOperation? operation)
+ {
+ return operation is ILiteralOperation literal && literal.ConstantValue.Value is 0;
+ }
+ }
+
+ ///
+ /// Returns true if the specified :
+ ///
+ /// Is an equals or not-equals operation
+ /// One operand is a literal zero
+ /// The other operand is any invocation of
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static bool IsStringStringCase(IBinaryOperation binaryOperation, RequiredSymbols symbols)
+ {
+ // Don't report a diagnostic if either the string.Compare overload or the
+ // corrasponding string.Equals overload is missing.
+ if (symbols.CompareStringString is null ||
+ symbols.EqualsStringString is null)
+ {
+ return false;
+ }
+
+ var invocation = GetInvocationFromEqualityCheckWithLiteralZero(binaryOperation);
+
+ return invocation is not null &&
+ invocation.TargetMethod.Equals(symbols.CompareStringString, SymbolEqualityComparer.Default);
+ }
+
+ ///
+ /// Returns true if the specified :
+ ///
+ /// Is an equals or not-equals operation
+ /// One operand is a literal zero
+ /// The other operand is an invocation of
+ /// The ignoreCase argument is a boolean literal
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static bool IsStringStringBoolCase(IBinaryOperation binaryOperation, RequiredSymbols symbols)
+ {
+ // Don't report a diagnostic if either the string.Compare overload or the
+ // corrasponding string.Equals overload is missing.
+ if (symbols.CompareStringStringBool is null ||
+ symbols.EqualsStringStringStringComparison is null)
+ {
+ return false;
+ }
+
+ var invocation = GetInvocationFromEqualityCheckWithLiteralZero(binaryOperation);
+
+ // Only report a diagnostic if the 'ignoreCase' argument is a boolean literal.
+ return invocation is not null &&
+ invocation.TargetMethod.Equals(symbols.CompareStringStringBool, SymbolEqualityComparer.Default) &&
+ invocation.Arguments.GetArgumentForParameterAtIndex(2).Value is ILiteralOperation literal &&
+ literal.ConstantValue.Value is bool;
+ }
+
+ ///
+ /// Returns true if the specified :
+ ///
+ /// Is an equals or not-equals operation
+ /// One operand is a literal zero
+ /// The other operand is any invocation of
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static bool IsStringStringStringComparisonCase(IBinaryOperation binaryOperation, RequiredSymbols symbols)
+ {
+ // Don't report a diagnostic if either the string.Compare overload or the
+ // corrasponding string.Equals overload is missing.
+ if (symbols.CompareStringStringStringComparison is null ||
+ symbols.EqualsStringStringStringComparison is null)
+ {
+ return false;
+ }
+
+ var invocation = GetInvocationFromEqualityCheckWithLiteralZero(binaryOperation);
+
+ return invocation is not null &&
+ invocation.TargetMethod.Equals(symbols.CompareStringStringStringComparison, SymbolEqualityComparer.Default);
+ }
+
+ // No IOperation instances are being stored here.
+#pragma warning disable RS1008 // Avoid storing per-compilation data into the fields of a diagnostic analyzer
+ private static readonly ImmutableArray> CaseSelectors =
+#pragma warning restore RS1008 // Avoid storing per-compilation data into the fields of a diagnostic analyzer
+ ImmutableArray.Create>(
+ IsStringStringCase,
+ IsStringStringBoolCase,
+ IsStringStringStringComparisonCase);
+ }
+}
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
index 68812c7030..8d0b4c0415 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
@@ -2397,6 +2397,26 @@
Pokud je to možné, zvažte použití řízení přístupu Azure na základě role namísto sdíleného přístupového podpisu (SAS). Pokud i přesto potřebujete používat sdílený přístupový podpis, zadejte SharedAccessProtocol.HttpsOnly.
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+
+
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+
+
+
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+
+
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+ Use 'AsSpan' with 'string.Concat'Use 'AsSpan' with 'string.Concat'
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf
index 0785cce804..06dd0a3669 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf
@@ -2397,6 +2397,26 @@
Erwägen Sie (sofern möglich) die Verwendung der rollenbasierten Zugriffssteuerung von Azure anstelle einer Shared Access Signature (SAS). Wenn Sie weiterhin eine SAS benötigen, verwenden Sie "SharedAccessProtocol.HttpsOnly".
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+
+
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+
+
+
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+
+
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+ Use 'AsSpan' with 'string.Concat'Use 'AsSpan' with 'string.Concat'
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf
index 0dddd9fbe3..a18be2a983 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf
@@ -2397,6 +2397,26 @@
Considere la posibilidad de usar el control de acceso basado en rol de Azure en lugar de una firma de acceso compartido (SAS), si es posible. Si tiene que usar una firma de acceso compartido, especifique SharedAccessProtocol.HttpsOnly.
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+
+
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+
+
+
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+
+
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+ Use 'AsSpan' with 'string.Concat'Use 'AsSpan' with 'string.Concat'
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf
index bab5e55d08..69cf068ab7 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf
@@ -2397,6 +2397,26 @@
Si possible, utilisez le contrôle d'accès en fonction du rôle d'Azure à la place d'une signature d'accès partagé. Si vous devez quand même utiliser une signature d'accès partagé, spécifiez SharedAccessProtocol.HttpsOnly.
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+
+
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+
+
+
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+
+
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+ Use 'AsSpan' with 'string.Concat'Use 'AsSpan' with 'string.Concat'
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf
index 08c50ace13..9e63ed4e7d 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf
@@ -2397,6 +2397,26 @@
Se possibile, provare a usare il controllo degli accessi in base al ruolo di Azure, invece della firma di accesso condiviso. Se è necessario usare una firma di accesso condiviso, specificare SharedAccessProtocol.HttpsOnly.
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+
+
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+
+
+
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+
+
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+ Use 'AsSpan' with 'string.Concat'Use 'AsSpan' with 'string.Concat'
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf
index e13f8d5717..cd0589990c 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf
@@ -2397,6 +2397,26 @@
可能な場合は、Shared Access Signature (SAS) の代わりに、Azure のロールベースのアクセス制御を使用することを検討してください。依然として SAS を使用する必要がある場合は、SharedAccessProtocol.HttpsOnly を指定します。
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+
+
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+
+
+
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+
+
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+ Use 'AsSpan' with 'string.Concat'Use 'AsSpan' with 'string.Concat'
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf
index e28b4caccc..c7b513992b 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf
@@ -2397,6 +2397,26 @@
가능한 경우 SAS(공유 액세스 서명) 대신 Azure의 역할 기반 액세스 제어를 사용하는 것이 좋습니다. 계속 SAS를 사용해야 하는 경우 SharedAccessProtocol.HttpsOnly를 지정하세요.
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+
+
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+ It is both clearer and likely faster to use 'string.Equals' instead of comparing the result of 'string.Compare' to zero.
+
+
+
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+ Use 'string.Equals' instead of comparing the result of 'string.Compare' to 0
+
+
+
+ Use 'string.Equals'
+ Use 'string.Equals'
+
+ Use 'AsSpan' with 'string.Concat'Use 'AsSpan' with 'string.Concat'
@@ -2509,4 +2529,4 @@