From c40c6e773c48c995fcea28b71ee1dcf3a4bd1a1b Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 1 Mar 2022 17:14:18 -0600 Subject: [PATCH] [core] use StringComparison.Ordinal everywhere (#4988) Context: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1307 Context: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1309 Context: https://github.com/dotnet/runtime/issues/43956 I was reviewing `dotnet trace` output of the .NET Podcast app: 6.32ms Microsoft.Maui.Controls!Microsoft.Maui.Controls.ShellNavigationManager.GetNavigationState 3.82ms Microsoft.Maui.Controls!Microsoft.Maui.Controls.ShellUriHandler.FormatUri The bulk of this time is spent in `string.StartsWith()`, doing a culture-aware string comparison? 3.82ms System.Private.CoreLib!System.String.StartsWith 2.57ms System.Private.CoreLib!System.Globalization.CultureInfo.get_CurrentCulture This looks to be showing the cost of the 1st culture-aware comparision plus any time making these slower string comparisons. It would be ideal if project templates did not even hit `CultureInfo.get_CurrentCulture`. To solve this, let's add to the `.editorconfig` file: dotnet_diagnostic.CA1307.severity = error dotnet_diagnostic.CA1309.severity = error And then fix all the places errors appear. There are some complications in projects that use `netstandard2.0`: 1. For `Contains()` use `IndexOf()` instead. 2. Use `#if NETSTANDARD2_0` for `GetHashCode()` or `Replace()`. Use the `char` overload of `string.Replace()` where possible. 3. Generally, `InvariantCulture` should not be used. After these changes, the `FormatUri` method disappears completely from the trace, and we instead get: 2.88ms Microsoft.Maui.Controls!Microsoft.Maui.Controls.ShellNavigationManager.GetNavigationState I suspect this saves ~3.44ms for any MAUI app startup, and a small amount more depending on the number of string comparisions happening. --- .editorconfig | 4 +++ .../src/Maui/Windows/WinUIWebViewManager.cs | 2 +- .../src/SharedSource/QueryStringHelper.cs | 4 ++- .../src/Android/TestCloudService.cs | 3 +- .../DemoFilteredItemSource.cs | 2 +- .../UITests.Shared/Utilities/ParsingUtils.cs | 2 +- .../ControlGallery/src/WinUI/WinUIStartup.cs | 2 +- .../ControlGallery/src/iOS/AppDelegate.cs | 2 +- .../src/Android/BorderBackgroundManager.cs | 22 ++++++------ .../Android/Renderers/DatePickerRenderer.cs | 2 +- .../Android/Renderers/TimePickerRenderer.cs | 2 +- .../Core/src/Android/ResourceManager.cs | 2 +- .../Core/src/Windows/DatePickerRenderer.cs | 8 ++--- .../src/Windows/FileImageSourceHandler.cs | 2 +- .../src/Windows/FontImageSourceHandler.cs | 2 +- .../Core/src/Windows/TimePickerRenderer.cs | 2 +- .../Core/src/Windows/WebViewRenderer.cs | 2 +- .../src/iOS/NativeViewPropertyListener.cs | 2 +- .../src/iOS/Renderers/DatePickerRenderer.cs | 6 ++-- .../src/iOS/Renderers/TimePickerRenderer.cs | 4 +-- .../src/iOS/Renderers/WkWebViewRenderer.cs | 4 +-- src/Controls/Maps/src/Pin.cs | 11 +++++- .../DataTemplateSelectorGallery.xaml.cs | 2 +- .../DemoFilteredItemSource.cs | 2 +- src/Controls/src/Core/Accelerator.cs | 10 +++++- src/Controls/src/Core/AnimatableKey.cs | 11 ++++-- .../src/Core/BindablePropertyConverter.cs | 2 +- src/Controls/src/Core/BindingExpression.cs | 4 +-- src/Controls/src/Core/BrushTypeConverter.cs | 11 +++--- .../src/Core/ExportEffectAttribute.cs | 2 +- src/Controls/src/Core/FontAttributes.cs | 2 +- src/Controls/src/Core/ResourceDictionary.cs | 2 +- .../src/Core/Shell/ShellNavigationManager.cs | 2 +- .../src/Core/Shell/ShellNavigationState.cs | 2 +- .../src/Core/Shell/ShellUriHandler.cs | 13 ++++--- src/Controls/src/Core/VisualElement.cs | 6 ++-- src/Controls/src/Core/WebView.cs | 2 +- .../src/Xaml/ApplyPropertiesVisitor.cs | 2 +- .../Xaml/MarkupExtensions/StaticExtension.cs | 2 +- src/Controls/src/Xaml/TypeArgumentsParser.cs | 5 +-- src/Controls/src/Xaml/XamlLoader.cs | 2 +- src/Controls/src/Xaml/XamlParser.cs | 4 +-- src/Controls/src/Xaml/XmlName.cs | 8 +++++ .../src/Xaml/XmlTypeXamlExtensions.cs | 2 +- .../Core.UnitTests/HostBuilderAppTests.cs | 2 +- .../Core.UnitTests/ShellUriHandlerTests.cs | 2 +- .../Xaml.UnitTests/Issues/Bz44216.xaml.cs | 2 +- .../Xaml.UnitTests/Issues/Gh3539.xaml.cs | 2 +- .../Xaml.UnitTests/MSBuild/MSBuildTests.cs | 4 +-- .../Validation/TypeMismatch.xaml.cs | 2 +- .../Converters/CornerRadiusTypeConverter.cs | 4 +-- .../src/Converters/ThicknessTypeConverter.cs | 4 +-- src/Core/src/Fonts/FontFile.cs | 12 +++++-- src/Core/src/Fonts/FontManager.Android.cs | 2 +- src/Core/src/Fonts/FontManager.Windows.cs | 2 +- .../Handlers/WebView/WebViewHandler.iOS.cs | 4 +-- .../FontImageSourceService.Windows.cs | 2 +- src/Core/src/Platform/Windows/MauiWebView.cs | 2 +- .../Platform/Windows/TimePickerExtensions.cs | 5 +-- .../src/Platform/iOS/TimePickerExtensions.cs | 27 ++++++++------- src/Core/src/Primitives/Font.cs | 2 +- .../ImageSource/ImageSourceServiceTests.cs | 6 ++-- .../UnitTests/AbstractViewHandlerTests.cs | 8 ++--- .../Hosting/HostBuilderFontsTests.cs | 4 +-- .../Hosting/HostBuilderServicesTests.cs | 2 +- .../UnitTests/PropertyMapperExtensionTests.cs | 15 ++++---- .../samples/Samples/View/BasePage.cs | 2 +- .../Samples/ViewModel/HomeViewModel.cs | 8 ++--- .../ViewModel/WebAuthenticatorViewModel.cs | 2 +- .../AppInfo/AppInfo.ios.tvos.watchos.macos.cs | 4 +-- .../src/Connectivity/Connectivity.android.cs | 11 +++--- .../src/DeviceInfo/DeviceInfo.android.cs | 34 +++++++++---------- .../src/DeviceInfo/DeviceInfo.uwp.cs | 2 +- .../src/MediaPicker/MediaPicker.ios.cs | 2 +- .../src/PhoneDialer/PhoneDialer.android.cs | 3 +- .../src/Preferences/Preferences.android.cs | 2 +- .../src/Types/DeviceIdiom.shared.cs | 6 +++- .../src/Types/DevicePlatform.shared.cs | 6 +++- .../src/Types/Shared/WebUtils.shared.cs | 6 ++-- .../Tests/Android/FileProvider_Tests.cs | 6 ++-- .../DeviceTests/Tests/DeviceInfo_Tests.cs | 10 +++--- .../test/DeviceTests/Tests/Geocoding_Tests.cs | 2 +- .../test/DeviceTests/Tests/Launcher_Tests.cs | 6 ++-- ...tectInvalidResourceOutputFilenamesTests.cs | 2 +- .../test/UnitTests/ResizetizeImagesTests.cs | 5 +-- .../VisualRunner/Sinks/DeviceExecutionSink.cs | 2 +- .../VisualRunner/Utils/TestRunLogger.cs | 2 +- .../EmbeddedResourceExtension.cs | 2 +- 88 files changed, 248 insertions(+), 182 deletions(-) diff --git a/.editorconfig b/.editorconfig index 184e44b0bd10..dfe41f162ddd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,6 +23,10 @@ indent_style = tab # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1416 dotnet_diagnostic.CA1416.severity = none +# Code analyzers +dotnet_diagnostic.CA1307.severity = error +dotnet_diagnostic.CA1309.severity = error + # Modifier preferences dotnet_style_require_accessibility_modifiers = never:suggestion diff --git a/src/BlazorWebView/src/Maui/Windows/WinUIWebViewManager.cs b/src/BlazorWebView/src/Maui/Windows/WinUIWebViewManager.cs index d011d994993b..aeb3cbb4490f 100644 --- a/src/BlazorWebView/src/Maui/Windows/WinUIWebViewManager.cs +++ b/src/BlazorWebView/src/Maui/Windows/WinUIWebViewManager.cs @@ -91,7 +91,7 @@ protected override async Task HandleWebResourceRequest(CoreWebView2WebResourceRe { relativePath = _hostPageRelativePath; } - relativePath = Path.Combine(_contentRootDir, relativePath.Replace("/", "\\")); + relativePath = Path.Combine(_contentRootDir, relativePath.Replace('/', '\\')); var winUIItem = await Package.Current.InstalledLocation.TryGetItemAsync(relativePath); if (winUIItem != null) diff --git a/src/BlazorWebView/src/SharedSource/QueryStringHelper.cs b/src/BlazorWebView/src/SharedSource/QueryStringHelper.cs index 99c964f0dcea..92f2f751af37 100644 --- a/src/BlazorWebView/src/SharedSource/QueryStringHelper.cs +++ b/src/BlazorWebView/src/SharedSource/QueryStringHelper.cs @@ -3,6 +3,8 @@ #nullable enable +using System; + namespace Microsoft.AspNetCore.Components.WebView { internal static class QueryStringHelper @@ -13,7 +15,7 @@ public static string RemovePossibleQueryString(string? url) { return string.Empty; } - var indexOfQueryString = url.IndexOf('?'); + var indexOfQueryString = url.IndexOf('?', StringComparison.Ordinal); return (indexOfQueryString == -1) ? url : url.Substring(0, indexOfQueryString); diff --git a/src/Compatibility/ControlGallery/src/Android/TestCloudService.cs b/src/Compatibility/ControlGallery/src/Android/TestCloudService.cs index f46472df4d91..27c68fbb0d69 100644 --- a/src/Compatibility/ControlGallery/src/Android/TestCloudService.cs +++ b/src/Compatibility/ControlGallery/src/Android/TestCloudService.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.Maui.Controls.Compatibility.ControlGallery; namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.Android @@ -8,7 +9,7 @@ public bool IsOnTestCloud() { var isInTestCloud = System.Environment.GetEnvironmentVariable("XAMARIN_TEST_CLOUD"); - return isInTestCloud != null && isInTestCloud.Equals("1"); + return isInTestCloud != null && isInTestCloud.Equals("1", StringComparison.Ordinal); } public string GetTestCloudDeviceName() diff --git a/src/Compatibility/ControlGallery/src/Core/GalleryPages/CollectionViewGalleries/DemoFilteredItemSource.cs b/src/Compatibility/ControlGallery/src/Core/GalleryPages/CollectionViewGalleries/DemoFilteredItemSource.cs index 405990fda559..95ea99843235 100644 --- a/src/Compatibility/ControlGallery/src/Core/GalleryPages/CollectionViewGalleries/DemoFilteredItemSource.cs +++ b/src/Compatibility/ControlGallery/src/Core/GalleryPages/CollectionViewGalleries/DemoFilteredItemSource.cs @@ -40,7 +40,7 @@ public DemoFilteredItemSource(int count = 50, Func windows .OnLaunching((_, e) => { - if (!string.IsNullOrWhiteSpace(e.Arguments) && e.Arguments.Contains("RunningAsUITests")) + if (!string.IsNullOrWhiteSpace(e.Arguments) && e.Arguments.Contains("RunningAsUITests", StringComparison.Ordinal)) { App.RunningAsUITests = true; ControlGallery.App.PreloadTestCasesIssuesList = false; diff --git a/src/Compatibility/ControlGallery/src/iOS/AppDelegate.cs b/src/Compatibility/ControlGallery/src/iOS/AppDelegate.cs index 957c7db68cf7..262ff325db1c 100644 --- a/src/Compatibility/ControlGallery/src/iOS/AppDelegate.cs +++ b/src/Compatibility/ControlGallery/src/iOS/AppDelegate.cs @@ -89,7 +89,7 @@ public bool IsOnTestCloud() { var isInTestCloud = Environment.GetEnvironmentVariable("XAMARIN_TEST_CLOUD"); - return isInTestCloud != null && isInTestCloud.Equals("1"); + return isInTestCloud != null && isInTestCloud.Equals("1", StringComparison.Ordinal); } public string GetTestCloudDeviceName() diff --git a/src/Compatibility/Core/src/Android/BorderBackgroundManager.cs b/src/Compatibility/Core/src/Android/BorderBackgroundManager.cs index b0bc13946214..f33321f30883 100644 --- a/src/Compatibility/Core/src/Android/BorderBackgroundManager.cs +++ b/src/Compatibility/Core/src/Android/BorderBackgroundManager.cs @@ -219,17 +219,17 @@ void BorderElementPropertyChanged(object sender, PropertyChangedEventArgs e) return; } - if (e.PropertyName.Equals(Button.BorderColorProperty.PropertyName) || - e.PropertyName.Equals(Button.BorderWidthProperty.PropertyName) || - e.PropertyName.Equals(Button.CornerRadiusProperty.PropertyName) || - e.PropertyName.Equals(VisualElement.BackgroundColorProperty.PropertyName) || - e.PropertyName.Equals(VisualElement.BackgroundProperty.PropertyName) || - e.PropertyName.Equals(Specifics.Button.UseDefaultPaddingProperty.PropertyName) || - e.PropertyName.Equals(Specifics.Button.UseDefaultShadowProperty.PropertyName) || - e.PropertyName.Equals(Specifics.ImageButton.IsShadowEnabledProperty.PropertyName) || - e.PropertyName.Equals(Specifics.ImageButton.ShadowColorProperty.PropertyName) || - e.PropertyName.Equals(Specifics.ImageButton.ShadowOffsetProperty.PropertyName) || - e.PropertyName.Equals(Specifics.ImageButton.ShadowRadiusProperty.PropertyName)) + if (e.PropertyName.Equals(Button.BorderColorProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(Button.BorderWidthProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(Button.CornerRadiusProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(VisualElement.BackgroundColorProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(VisualElement.BackgroundProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(Specifics.Button.UseDefaultPaddingProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(Specifics.Button.UseDefaultShadowProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(Specifics.ImageButton.IsShadowEnabledProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(Specifics.ImageButton.ShadowColorProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(Specifics.ImageButton.ShadowOffsetProperty.PropertyName, StringComparison.Ordinal) || + e.PropertyName.Equals(Specifics.ImageButton.ShadowRadiusProperty.PropertyName, StringComparison.Ordinal)) { Reset(); UpdateDrawable(); diff --git a/src/Compatibility/Core/src/Android/Renderers/DatePickerRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/DatePickerRenderer.cs index 00dd9371e617..36fda50bea44 100644 --- a/src/Compatibility/Core/src/Android/Renderers/DatePickerRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/DatePickerRenderer.cs @@ -160,7 +160,7 @@ void SetDate(DateTime date) { EditText.Text = date.ToShortDateString(); } - else if (Element.Format.Contains('/')) + else if (Element.Format.Contains('/', StringComparison.Ordinal)) { EditText.Text = date.ToString(Element.Format, CultureInfo.InvariantCulture); } diff --git a/src/Compatibility/Core/src/Android/Renderers/TimePickerRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/TimePickerRenderer.cs index 8edc9601cf5a..b907c5ca0471 100644 --- a/src/Compatibility/Core/src/Android/Renderers/TimePickerRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/TimePickerRenderer.cs @@ -20,7 +20,7 @@ public abstract class TimePickerRendererBase : ViewRenderer (DateFormat.Is24HourFormat(Context) && Element.Format == (string)TimePicker.FormatProperty.DefaultValue) || Element.Format?.Contains('H') == true; + get => (DateFormat.Is24HourFormat(Context) && Element.Format == (string)TimePicker.FormatProperty.DefaultValue) || Element.Format?.Contains('H', StringComparison.Ordinal) == true; } public TimePickerRendererBase(Context context) : base(context) diff --git a/src/Compatibility/Core/src/Android/ResourceManager.cs b/src/Compatibility/Core/src/Android/ResourceManager.cs index 7340b6a87203..d2fb7df45fe9 100644 --- a/src/Compatibility/Core/src/Android/ResourceManager.cs +++ b/src/Compatibility/Core/src/Android/ResourceManager.cs @@ -405,7 +405,7 @@ static int IdFromTitle(string title, [DynamicallyAccessedMembers(DynamicallyAcce // When searching by reflection you would use a "_" instead of a "." // So this accounts for cases where users were searching with an "_" - if ((id = SearchByIdentifier(name.Replace("_", "."), defType, resource, packageName)) > 0) + if ((id = SearchByIdentifier(name.Replace("_", ".", StringComparison.Ordinal), defType, resource, packageName)) > 0) return id; int SearchByIdentifier(string n, string d, Resources r, string p) diff --git a/src/Compatibility/Core/src/Windows/DatePickerRenderer.cs b/src/Compatibility/Core/src/Windows/DatePickerRenderer.cs index 09681d8091eb..332726c6741a 100644 --- a/src/Compatibility/Core/src/Windows/DatePickerRenderer.cs +++ b/src/Compatibility/Core/src/Windows/DatePickerRenderer.cs @@ -143,7 +143,7 @@ void OnControlDateChanged(object sender, DatePickerValueChangedEventArgs e) bool CheckDateFormat() { - return String.IsNullOrWhiteSpace(Element.Format) || Element.Format.Equals("d"); + return String.IsNullOrWhiteSpace(Element.Format) || Element.Format.Equals("d", StringComparison.Ordinal); } [PortHandler] @@ -165,7 +165,7 @@ void UpdateMonth() { Control.MonthFormat = "month"; } - else if (Element.Format.Equals("D")) + else if (Element.Format.Equals("D", StringComparison.Ordinal)) { Control.MonthFormat = "month.full"; } @@ -191,7 +191,7 @@ void UpdateDay() { Control.DayFormat = "day"; } - else if (Element.Format.Equals("D")) + else if (Element.Format.Equals("D", StringComparison.Ordinal)) { Control.DayFormat = "dayofweek.full"; } @@ -217,7 +217,7 @@ void UpdateYear() { Control.YearFormat = "year"; } - else if (Element.Format.Equals("D")) + else if (Element.Format.Equals("D", StringComparison.Ordinal)) { Control.YearFormat = "year.full"; } diff --git a/src/Compatibility/Core/src/Windows/FileImageSourceHandler.cs b/src/Compatibility/Core/src/Windows/FileImageSourceHandler.cs index d0baecfd3e1a..67812728c302 100644 --- a/src/Compatibility/Core/src/Windows/FileImageSourceHandler.cs +++ b/src/Compatibility/Core/src/Windows/FileImageSourceHandler.cs @@ -70,7 +70,7 @@ void UpdateImageDirectory(FileImageSource fileSource) var directory = IOPath.GetDirectoryName(filePath); - if (string.IsNullOrEmpty(directory) || !IOPath.GetFullPath(directory).Equals(IOPath.GetFullPath(imageDirectory))) + if (string.IsNullOrEmpty(directory) || !IOPath.GetFullPath(directory).Equals(IOPath.GetFullPath(imageDirectory), StringComparison.Ordinal)) { filePath = IOPath.Combine(imageDirectory, filePath); fileSource.File = filePath; diff --git a/src/Compatibility/Core/src/Windows/FontImageSourceHandler.cs b/src/Compatibility/Core/src/Windows/FontImageSourceHandler.cs index 94c4679de0fa..d9aae093bd71 100644 --- a/src/Compatibility/Core/src/Windows/FontImageSourceHandler.cs +++ b/src/Compatibility/Core/src/Windows/FontImageSourceHandler.cs @@ -124,7 +124,7 @@ string GetFontSource(FontImageSource fontImageSource) foreach(var family in allFamilies) { - if(family.Contains(source)) + if(family.Contains(source, StringComparison.Ordinal)) { fontSource = family; break; diff --git a/src/Compatibility/Core/src/Windows/TimePickerRenderer.cs b/src/Compatibility/Core/src/Windows/TimePickerRenderer.cs index 1db347939771..db39577aa909 100644 --- a/src/Compatibility/Core/src/Windows/TimePickerRenderer.cs +++ b/src/Compatibility/Core/src/Windows/TimePickerRenderer.cs @@ -168,7 +168,7 @@ void UpdateFont() void UpdateTime() { Control.Time = Element.Time; - if (Element.Format?.Contains('H') == true) + if (Element.Format?.Contains('H', StringComparison.Ordinal) == true) { Control.ClockIdentifier = "24HourClock"; } diff --git a/src/Compatibility/Core/src/Windows/WebViewRenderer.cs b/src/Compatibility/Core/src/Windows/WebViewRenderer.cs index 118592f955ca..20860424b73f 100644 --- a/src/Compatibility/Core/src/Windows/WebViewRenderer.cs +++ b/src/Compatibility/Core/src/Windows/WebViewRenderer.cs @@ -52,7 +52,7 @@ public void LoadHtml(string html, string baseUrl) _internalWebView.NavigationCompleted += async (sender, args) => { // Generate a version of the script with the correct tag - var script = BaseInsertionScript.Replace("baseTag", baseTag); + var script = BaseInsertionScript.Replace("baseTag", baseTag, StringComparison.Ordinal); // Run it and retrieve the updated HTML from our WebView await sender.ExecuteScriptAsync(script); diff --git a/src/Compatibility/Core/src/iOS/NativeViewPropertyListener.cs b/src/Compatibility/Core/src/iOS/NativeViewPropertyListener.cs index a3c58836a0c2..cb638b63dbd5 100644 --- a/src/Compatibility/Core/src/iOS/NativeViewPropertyListener.cs +++ b/src/Compatibility/Core/src/iOS/NativeViewPropertyListener.cs @@ -22,7 +22,7 @@ public NativeViewPropertyListener(string targetProperty) public override void ObserveValue(NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context) { - if (keyPath.ToString().Equals(TargetProperty, StringComparison.InvariantCultureIgnoreCase)) + if (keyPath.ToString().Equals(TargetProperty, StringComparison.OrdinalIgnoreCase)) PropertyChanged?.Invoke(ofObject, new PropertyChangedEventArgs(TargetProperty)); else base.ObserveValue(keyPath, ofObject, change, context); diff --git a/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs index bfe22d667e2e..cf45d5e46c59 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs @@ -176,12 +176,12 @@ void UpdateDateFromModel(bool animate) _picker.SetDate(Element.Date.ToNSDate(), animate); // Can't use Element.Format because it won't display the correct format if the region and language are set differently - if (string.IsNullOrWhiteSpace(Element.Format) || Element.Format.Equals("d") || Element.Format.Equals("D")) + if (string.IsNullOrWhiteSpace(Element.Format) || Element.Format.Equals("d", StringComparison.OrdinalIgnoreCase)) { NSDateFormatter dateFormatter = new NSDateFormatter(); dateFormatter.TimeZone = NSTimeZone.FromGMT(0); - if (Element.Format?.Equals("D") == true) + if (Element.Format?.Equals("D", StringComparison.Ordinal) == true) { dateFormatter.DateStyle = NSDateFormatterStyle.Long; var strDate = dateFormatter.StringFor(_picker.Date); @@ -194,7 +194,7 @@ void UpdateDateFromModel(bool animate) Control.Text = strDate; } } - else if (Element.Format.Contains("/")) + else if (Element.Format.Contains('/', StringComparison.Ordinal)) { Control.Text = Element.Date.ToString(Element.Format, CultureInfo.InvariantCulture); } diff --git a/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs index 34654c04564f..8d25655b76d9 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs @@ -233,13 +233,13 @@ void UpdateTime() Control.Text = DateTime.Today.Add(Element.Time).ToString(Element.Format, cultureInfos); } - if (Element.Format?.Contains('H') == true) + if (Element.Format?.Contains('H', StringComparison.Ordinal) == true) { var ci = new System.Globalization.CultureInfo("de-DE"); NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName); _picker.Locale = locale; } - else if (Element.Format?.Contains('h') == true) + else if (Element.Format?.Contains('h', StringComparison.Ordinal) == true) { var ci = new System.Globalization.CultureInfo("en-US"); NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName); diff --git a/src/Compatibility/Core/src/iOS/Renderers/WkWebViewRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/WkWebViewRenderer.cs index 0278e0f62237..f6a373728fb3 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/WkWebViewRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/WkWebViewRenderer.cs @@ -365,7 +365,7 @@ async Task> GetCookiesFromNativeStore(string url) { // we don't care that much about this being accurate // the cookie container will split the cookies up more correctly - if (!cookie.Domain.Contains(domain) && !domain.Contains(cookie.Domain)) + if (!cookie.Domain.Contains(domain, StringComparison.Ordinal) && !domain.Contains(cookie.Domain, StringComparison.Ordinal)) continue; existingCookies.Add(cookie); @@ -563,7 +563,7 @@ async Task DeleteCookies(List cookies) foreach (var deleteme in cookies) { - if (record.DisplayName.Contains(deleteme.Domain) || deleteme.Domain.Contains(record.DisplayName)) + if (record.DisplayName.Contains(deleteme.Domain, StringComparison.Ordinal) || deleteme.Domain.Contains(record.DisplayName, StringComparison.Ordinal)) { WKWebsiteDataStore.DefaultDataStore.RemoveDataOfTypes(record.DataTypes, new[] { record }, () => { }); diff --git a/src/Controls/Maps/src/Pin.cs b/src/Controls/Maps/src/Pin.cs index ea6ad8050ae2..365295b45a09 100644 --- a/src/Controls/Maps/src/Pin.cs +++ b/src/Controls/Maps/src/Pin.cs @@ -70,10 +70,17 @@ public override int GetHashCode() { unchecked { +#if NETSTANDARD2_0 int hashCode = Label?.GetHashCode() ?? 0; hashCode = (hashCode * 397) ^ Position.GetHashCode(); hashCode = (hashCode * 397) ^ (int)Type; hashCode = (hashCode * 397) ^ (Address?.GetHashCode() ?? 0); +#else + int hashCode = Label?.GetHashCode(StringComparison.Ordinal) ?? 0; + hashCode = (hashCode * 397) ^ Position.GetHashCode(); + hashCode = (hashCode * 397) ^ (int)Type; + hashCode = (hashCode * 397) ^ (Address?.GetHashCode(StringComparison.Ordinal) ?? 0); +#endif return hashCode; } } @@ -106,7 +113,9 @@ public bool SendInfoWindowClick() bool Equals(Pin other) { - return string.Equals(Label, other.Label) && Equals(Position, other.Position) && Type == other.Type && string.Equals(Address, other.Address); + return string.Equals(Label, other.Label, StringComparison.Ordinal) && + Equals(Position, other.Position) && Type == other.Type && + string.Equals(Address, other.Address, StringComparison.Ordinal); } } } diff --git a/src/Controls/samples/Controls.Sample/Pages/Controls/CollectionViewGalleries/DataTemplateSelectorGallery.xaml.cs b/src/Controls/samples/Controls.Sample/Pages/Controls/CollectionViewGalleries/DataTemplateSelectorGallery.xaml.cs index 2dfd6892ad76..3eb7438d85ba 100644 --- a/src/Controls/samples/Controls.Sample/Pages/Controls/CollectionViewGalleries/DataTemplateSelectorGallery.xaml.cs +++ b/src/Controls/samples/Controls.Sample/Pages/Controls/CollectionViewGalleries/DataTemplateSelectorGallery.xaml.cs @@ -36,7 +36,7 @@ private bool ItemMatches(string filter, CollectionViewGalleryTestItem item) return true; } - return item.Date.DayOfWeek.ToString().ToLower().Contains(filter.ToLower()); + return item.Date.DayOfWeek.ToString().Contains(filter, StringComparison.OrdinalIgnoreCase); } } diff --git a/src/Controls/samples/Controls.Sample/Pages/Controls/CollectionViewGalleries/DemoFilteredItemSource.cs b/src/Controls/samples/Controls.Sample/Pages/Controls/CollectionViewGalleries/DemoFilteredItemSource.cs index 7dac87fb1400..4cf08b3735ef 100644 --- a/src/Controls/samples/Controls.Sample/Pages/Controls/CollectionViewGalleries/DemoFilteredItemSource.cs +++ b/src/Controls/samples/Controls.Sample/Pages/Controls/CollectionViewGalleries/DemoFilteredItemSource.cs @@ -40,7 +40,7 @@ public DemoFilteredItemSource(int count = 50, Func public override int GetHashCode() { - return ToString().GetHashCode(); +#if NETSTANDARD2_0 + return _text.GetHashCode(); +#else + return _text.GetHashCode(StringComparison.Ordinal); +#endif } public static implicit operator Accelerator(string accelerator) diff --git a/src/Controls/src/Core/AnimatableKey.cs b/src/Controls/src/Core/AnimatableKey.cs index 0fe8d50b0f77..68b3ed0501eb 100644 --- a/src/Controls/src/Core/AnimatableKey.cs +++ b/src/Controls/src/Core/AnimatableKey.cs @@ -47,18 +47,25 @@ public override int GetHashCode() unchecked { IAnimatable target; +#if NETSTANDARD2_0 if (!Animatable.TryGetTarget(out target)) { return Handle?.GetHashCode() ?? 0; } - return ((target?.GetHashCode() ?? 0) * 397) ^ (Handle?.GetHashCode() ?? 0); +#else + if (!Animatable.TryGetTarget(out target)) + { + return Handle?.GetHashCode(StringComparison.Ordinal) ?? 0; + } + return ((target?.GetHashCode() ?? 0) * 397) ^ (Handle?.GetHashCode(StringComparison.Ordinal) ?? 0); +#endif } } protected bool Equals(AnimatableKey other) { - if (!string.Equals(Handle, other.Handle)) + if (!string.Equals(Handle, other.Handle, StringComparison.Ordinal)) { return false; } diff --git a/src/Controls/src/Core/BindablePropertyConverter.cs b/src/Controls/src/Core/BindablePropertyConverter.cs index 43dbb5fb146f..5822574f59ab 100644 --- a/src/Controls/src/Core/BindablePropertyConverter.cs +++ b/src/Controls/src/Core/BindablePropertyConverter.cs @@ -81,7 +81,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c if (string.IsNullOrWhiteSpace(strValue)) return null; - if (strValue.Contains(":")) + if (strValue.IndexOf(":", StringComparison.Ordinal) != -1) { Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning("Can't resolve properties with xml namespace prefix."); return null; diff --git a/src/Controls/src/Core/BindingExpression.cs b/src/Controls/src/Core/BindingExpression.cs index 7924d457e5c5..4b120fed7762 100644 --- a/src/Controls/src/Core/BindingExpression.cs +++ b/src/Controls/src/Core/BindingExpression.cs @@ -213,7 +213,7 @@ void ParsePath() BindingExpressionPart indexer = null; - int lbIndex = part.IndexOf('['); + int lbIndex = part.IndexOf("[", StringComparison.Ordinal); if (lbIndex != -1) { int rbIndex = part.LastIndexOf(']'); @@ -745,7 +745,7 @@ public void PropertyChanged(object sender, PropertyChangedEventArgs args) { if (part.IsIndexer) { - if (name.Contains("[")) + if (name.IndexOf("[", StringComparison.Ordinal) != -1) { if (name != string.Format("{0}[{1}]", part.IndexerName, part.Content)) return; diff --git a/src/Controls/src/Core/BrushTypeConverter.cs b/src/Controls/src/Core/BrushTypeConverter.cs index a9c1f93cb581..7e14cd70d461 100644 --- a/src/Controls/src/Core/BrushTypeConverter.cs +++ b/src/Controls/src/Core/BrushTypeConverter.cs @@ -94,7 +94,12 @@ public GradientBrush Parse(string css) return _gradient; } - _parts = css.Replace("\r\n", "").Split(new[] { '(', ')', ',' }, StringSplitOptions.RemoveEmptyEntries); +#if NETSTANDARD2_0 + _parts = css.Replace("\r\n", "") +#else + _parts = css.Replace("\r\n", "", StringComparison.Ordinal) +#endif + .Split(new[] { '(', ')', ',' }, StringSplitOptions.RemoveEmptyEntries); while (_position < _parts.Length) { @@ -306,9 +311,7 @@ Point GetGradientCenter() if (parts.Length > gradientCenterPosition) { - var at = parts[gradientCenterPosition].Trim(); - - if (at.Contains("at")) + if (parts[gradientCenterPosition].IndexOf("at", StringComparison.Ordinal) != -1) { gradientCenterPosition++; var directionX = gradientCenterPosition < parts.Length ? parts[gradientCenterPosition].Trim() : string.Empty; diff --git a/src/Controls/src/Core/ExportEffectAttribute.cs b/src/Controls/src/Core/ExportEffectAttribute.cs index ecc20e207312..aa1f59e9f93e 100644 --- a/src/Controls/src/Core/ExportEffectAttribute.cs +++ b/src/Controls/src/Core/ExportEffectAttribute.cs @@ -9,7 +9,7 @@ public sealed class ExportEffectAttribute : Attribute /// public ExportEffectAttribute(Type effectType, string uniqueName) { - if (uniqueName.Contains(".")) + if (uniqueName.IndexOf(".", StringComparison.Ordinal) != -1) throw new ArgumentException("uniqueName must not contain a ."); Type = effectType; Id = uniqueName; diff --git a/src/Controls/src/Core/FontAttributes.cs b/src/Controls/src/Core/FontAttributes.cs index cfa16cf88dc9..06ab1385c192 100644 --- a/src/Controls/src/Core/FontAttributes.cs +++ b/src/Controls/src/Core/FontAttributes.cs @@ -38,7 +38,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c FontAttributes attributes = FontAttributes.None; strValue = strValue.Trim(); - if (strValue.Contains(",")) + if (strValue.IndexOf(",", StringComparison.Ordinal) != -1) { //Xaml foreach (var part in strValue.Split(',')) attributes |= ParseSingleAttribute(part, strValue); diff --git a/src/Controls/src/Core/ResourceDictionary.cs b/src/Controls/src/Core/ResourceDictionary.cs index f90bff495525..01e880f206da 100644 --- a/src/Controls/src/Core/ResourceDictionary.cs +++ b/src/Controls/src/Core/ResourceDictionary.cs @@ -363,7 +363,7 @@ object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceP var rootTargetPath = XamlResourceIdAttribute.GetPathForType(rootObjectType); var assembly = rootObjectType.GetTypeInfo().Assembly; - if (value.Contains(";assembly=")) + if (value.IndexOf(";assembly=", StringComparison.Ordinal) != -1) { var parts = value.Split(new[] { ";assembly=" }, StringSplitOptions.RemoveEmptyEntries); value = parts[0]; diff --git a/src/Controls/src/Core/Shell/ShellNavigationManager.cs b/src/Controls/src/Core/Shell/ShellNavigationManager.cs index 2f15dd43ea72..4162b89b5346 100644 --- a/src/Controls/src/Core/Shell/ShellNavigationManager.cs +++ b/src/Controls/src/Core/Shell/ShellNavigationManager.cs @@ -243,7 +243,7 @@ public static void ApplyQueryAttributes(Element element, ShellRouteParameters qu if (!q.Key.StartsWith(prefix, StringComparison.Ordinal)) continue; var key = q.Key.Substring(prefix.Length); - if (key.Contains(".")) + if (key.IndexOf(".", StringComparison.Ordinal) != -1) continue; filteredQuery.Add(key, q.Value); } diff --git a/src/Controls/src/Core/Shell/ShellNavigationState.cs b/src/Controls/src/Core/Shell/ShellNavigationState.cs index 0bffe3ddc784..0d3768c4ed0d 100644 --- a/src/Controls/src/Core/Shell/ShellNavigationState.cs +++ b/src/Controls/src/Core/Shell/ShellNavigationState.cs @@ -64,7 +64,7 @@ static Uri TrimDownImplicitAndDefaultPaths(Uri uri) uri = ShellUriHandler.FormatUri(uri, null); // don't trim relative pushes - if (!uri.OriginalString.StartsWith($"{Routing.PathSeparator}{Routing.PathSeparator}")) + if (!uri.OriginalString.StartsWith("//", StringComparison.Ordinal)) return uri; string[] parts = uri.OriginalString.TrimEnd(Routing.PathSeparator[0]).Split(Routing.PathSeparator[0]); diff --git a/src/Controls/src/Core/Shell/ShellUriHandler.cs b/src/Controls/src/Core/Shell/ShellUriHandler.cs index 7fec84dcc77e..c82690acf386 100644 --- a/src/Controls/src/Core/Shell/ShellUriHandler.cs +++ b/src/Controls/src/Core/Shell/ShellUriHandler.cs @@ -15,7 +15,7 @@ internal class ShellUriHandler internal static Uri FormatUri(Uri path, Shell shell) { - if (path.OriginalString.StartsWith("..") && shell?.CurrentState != null) + if (path.OriginalString.StartsWith("..", StringComparison.Ordinal) && shell?.CurrentState != null) { var pathAndQueryString = path.OriginalString.Split(new[] { '?' }, 2); string pathPart = pathAndQueryString[0]; @@ -334,7 +334,12 @@ internal static List GenerateRoutePaths(Shell shell, Uri re var uri = ConvertToStandardFormat(shell, CreateUri(route)); if (uri.Equals(request)) { - throw new Exception($"Global routes currently cannot be the only page on the stack, so absolute routing to global routes is not supported. For now, just navigate to: {originalRequest.OriginalString.Replace("//", "")}"); + #if NETSTANDARD2_0 + var replaced = originalRequest.OriginalString.Replace("//", ""); + #else + var replaced = originalRequest.OriginalString.Replace("//", "", StringComparison.Ordinal); + #endif + throw new Exception($"Global routes currently cannot be the only page on the stack, so absolute routing to global routes is not supported. For now, just navigate to: {replaced}"); } } } @@ -535,7 +540,7 @@ static bool FindAndAddSegmentMatch(RouteRequestBuilder possibleRoutePath, string var collapsedRoutes = CollapsePath(routeKey, possibleRoutePath.SegmentsMatched, true); var collapsedRoute = String.Join(_pathSeparator, collapsedRoutes); - if (routeKey.StartsWith("//")) + if (routeKey.StartsWith("//", StringComparison.Ordinal)) { var routeKeyPaths = routeKey.Split(_pathSeparators, StringSplitOptions.RemoveEmptyEntries); @@ -578,7 +583,7 @@ static bool FindAndAddSegmentMatch(RouteRequestBuilder possibleRoutePath, string var collapsedLeafRoute = String.Join(_pathSeparator, CollapsePath(routeKey, leafSearch.SegmentsMatched, true)); - if (routeKey.StartsWith("//")) + if (routeKey.StartsWith("//", StringComparison.Ordinal)) collapsedLeafRoute = "//" + collapsedLeafRoute; string segmentMatch = leafSearch.GetNextSegmentMatch(collapsedLeafRoute); diff --git a/src/Controls/src/Core/VisualElement.cs b/src/Controls/src/Core/VisualElement.cs index 8993970810d9..6f9825854cfd 100644 --- a/src/Controls/src/Core/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement.cs @@ -174,10 +174,10 @@ static void OnTransformChanged(BindableObject bindable, object oldValue, object var transforms = ((string)newValue).Split(' '); foreach (var transform in transforms) { - if (string.IsNullOrEmpty(transform) || transform.IndexOf('(') < 0 || transform.IndexOf(')') < 0) + if (string.IsNullOrEmpty(transform) || transform.IndexOf("(", StringComparison.Ordinal) < 0 || transform.IndexOf(")", StringComparison.Ordinal) < 0) throw new FormatException("Format for transform is 'none | transform(value) [transform(value) ]*'"); - var transformName = transform.Substring(0, transform.IndexOf('(')); - var value = transform.Substring(transform.IndexOf('(') + 1, transform.IndexOf(')') - transform.IndexOf('(') - 1); + var transformName = transform.Substring(0, transform.IndexOf("(", StringComparison.Ordinal)); + var value = transform.Substring(transform.IndexOf("(", StringComparison.Ordinal) + 1, transform.IndexOf(")", StringComparison.Ordinal) - transform.IndexOf("(", StringComparison.Ordinal) - 1); double translationX, translationY, scaleX, scaleY, rotateX, rotateY, rotate; if (transformName.StartsWith("translateX", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out translationX)) bindable.SetValue(TranslationXProperty, translationX); diff --git a/src/Controls/src/Core/WebView.cs b/src/Controls/src/Core/WebView.cs index f47acbab9c82..cfae3d5bc4b9 100644 --- a/src/Controls/src/Core/WebView.cs +++ b/src/Controls/src/Core/WebView.cs @@ -247,7 +247,7 @@ static string EscapeJsString(string js) if (js == null) return null; - if (!js.Contains("'")) + if (js.IndexOf("'", StringComparison.Ordinal) == -1) return js; //get every quote in the string along with all the backslashes preceding it diff --git a/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs b/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs index 8493e9fc6283..188d65de0153 100644 --- a/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs +++ b/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs @@ -296,7 +296,7 @@ static string GetContentPropertyName(IEnumerable attributes static bool GetRealNameAndType(ref Type elementType, string namespaceURI, ref string localname, object rootElement, IXmlLineInfo lineInfo) { - var dotIdx = localname.IndexOf('.'); + var dotIdx = localname.IndexOf(".", StringComparison.Ordinal); if (dotIdx > 0) { var typename = localname.Substring(0, dotIdx); diff --git a/src/Controls/src/Xaml/MarkupExtensions/StaticExtension.cs b/src/Controls/src/Xaml/MarkupExtensions/StaticExtension.cs index 7a37e3713908..2c8101430eb8 100644 --- a/src/Controls/src/Xaml/MarkupExtensions/StaticExtension.cs +++ b/src/Controls/src/Xaml/MarkupExtensions/StaticExtension.cs @@ -17,7 +17,7 @@ public object ProvideValue(IServiceProvider serviceProvider) throw new ArgumentNullException(nameof(serviceProvider)); if (!(serviceProvider.GetService(typeof(IXamlTypeResolver)) is IXamlTypeResolver typeResolver)) throw new ArgumentException("No IXamlTypeResolver in IServiceProvider"); - if (string.IsNullOrEmpty(Member) || !Member.Contains(".")) + if (string.IsNullOrEmpty(Member) || Member.IndexOf(".", StringComparison.Ordinal) == -1) throw new XamlParseException("Syntax for x:Static is [Member=][prefix:]typeName.staticMemberName", serviceProvider); var dotIdx = Member.LastIndexOf('.'); diff --git a/src/Controls/src/Xaml/TypeArgumentsParser.cs b/src/Controls/src/Xaml/TypeArgumentsParser.cs index 8d6c757ef60f..4fc536ba210b 100644 --- a/src/Controls/src/Xaml/TypeArgumentsParser.cs +++ b/src/Controls/src/Xaml/TypeArgumentsParser.cs @@ -1,4 +1,5 @@ #nullable disable +using System; using System.Collections.Generic; using System.Xml; @@ -45,8 +46,8 @@ static XmlType Parse(string match, ref string remaining, IXmlNamespaceResolver r if (isGeneric) { typeArguments = ParseExpression( - type.Substring(type.IndexOf('(') + 1, type.LastIndexOf(')') - type.IndexOf('(') - 1), resolver, lineinfo); - type = type.Substring(0, type.IndexOf('(')); + type.Substring(type.IndexOf("(", StringComparison.Ordinal) + 1, type.LastIndexOf(")", StringComparison.Ordinal) - type.IndexOf("(", StringComparison.Ordinal) - 1), resolver, lineinfo); + type = type.Substring(0, type.IndexOf("(", StringComparison.Ordinal)); } var split = type.Split(':'); diff --git a/src/Controls/src/Xaml/XamlLoader.cs b/src/Controls/src/Xaml/XamlLoader.cs index 54344b28d083..e38f82de6dec 100644 --- a/src/Controls/src/Xaml/XamlLoader.cs +++ b/src/Controls/src/Xaml/XamlLoader.cs @@ -355,7 +355,7 @@ static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTarg var pattern = $"x:Class *= *\"{type.FullName}\""; var regex = new Regex(pattern, RegexOptions.ECMAScript); - if (regex.IsMatch(xaml) || xaml.Contains($"x:Class=\"{type.FullName}\"")) + if (regex.IsMatch(xaml) || xaml.IndexOf($"x:Class=\"{type.FullName}\"") != -1) return xaml; } return null; diff --git a/src/Controls/src/Xaml/XamlParser.cs b/src/Controls/src/Xaml/XamlParser.cs index 4df10bdb2d30..7156f32baee7 100644 --- a/src/Controls/src/Xaml/XamlParser.cs +++ b/src/Controls/src/Xaml/XamlParser.cs @@ -65,7 +65,7 @@ static void ParseXamlElementFor(IElementNode node, XmlReader reader) return; case XmlNodeType.Element: // 1. Property Element. - if (reader.Name.Contains(".")) + if (reader.Name.IndexOf(".", StringComparison.Ordinal) != -1) { XmlName name; if (reader.Name.StartsWith(elementName + ".", StringComparison.Ordinal)) @@ -215,7 +215,7 @@ static IList> ParseXamlAttributes(XmlReader reader, } var namespaceUri = reader.NamespaceURI; - if (reader.LocalName.Contains(".") && namespaceUri == "") + if (reader.LocalName.IndexOf(".", StringComparison.Ordinal) != -1 && namespaceUri == "") namespaceUri = ((IXmlNamespaceResolver)reader).LookupNamespace(""); var propertyName = ParsePropertyName(new XmlName(namespaceUri, reader.LocalName)); diff --git a/src/Controls/src/Xaml/XmlName.cs b/src/Controls/src/Xaml/XmlName.cs index b2df98ce7671..017cf8fb9bdc 100644 --- a/src/Controls/src/Xaml/XmlName.cs +++ b/src/Controls/src/Xaml/XmlName.cs @@ -1,4 +1,5 @@ #nullable disable +using System; using System.Diagnostics; namespace Microsoft.Maui.Controls.Xaml @@ -42,10 +43,17 @@ public override int GetHashCode() unchecked { int hashCode = 0; +#if NETSTANDARD2_0 if (NamespaceURI != null) hashCode = NamespaceURI.GetHashCode(); if (LocalName != null) hashCode = (hashCode * 397) ^ LocalName.GetHashCode(); +#else + if (NamespaceURI != null) + hashCode = NamespaceURI.GetHashCode(StringComparison.Ordinal); + if (LocalName != null) + hashCode = (hashCode * 397) ^ LocalName.GetHashCode(StringComparison.Ordinal); +#endif return hashCode; } } diff --git a/src/Controls/src/Xaml/XmlTypeXamlExtensions.cs b/src/Controls/src/Xaml/XmlTypeXamlExtensions.cs index b15f559341a4..9c823998fee0 100644 --- a/src/Controls/src/Xaml/XmlTypeXamlExtensions.cs +++ b/src/Controls/src/Xaml/XmlTypeXamlExtensions.cs @@ -68,7 +68,7 @@ static class XmlTypeXamlExtensions for (var i = 0; i < lookupNames.Count; i++) { var name = lookupNames[i]; - if (name.Contains(":")) + if (name.IndexOf(":", StringComparison.Ordinal) != -1) name = name.Substring(name.LastIndexOf(':') + 1); if (typeArguments != null) name += "`" + typeArguments.Count; //this will return an open generic Type diff --git a/src/Controls/tests/Core.UnitTests/HostBuilderAppTests.cs b/src/Controls/tests/Core.UnitTests/HostBuilderAppTests.cs index 35c5b5f27ee4..0a5c0a5dcef1 100644 --- a/src/Controls/tests/Core.UnitTests/HostBuilderAppTests.cs +++ b/src/Controls/tests/Core.UnitTests/HostBuilderAppTests.cs @@ -85,7 +85,7 @@ public void ConfigurationGetDebugViewWorks() using var app = builder.Build(); // Make sure we don't lose "MemoryConfigurationProvider" from GetDebugView() when wrapping the provider. - Assert.True(((IConfigurationRoot)app.Configuration).GetDebugView().Contains("foo=bar (MemoryConfigurationProvider)")); + Assert.True(((IConfigurationRoot)app.Configuration).GetDebugView().Contains("foo=bar (MemoryConfigurationProvider)", StringComparison.Ordinal)); } [Test] diff --git a/src/Controls/tests/Core.UnitTests/ShellUriHandlerTests.cs b/src/Controls/tests/Core.UnitTests/ShellUriHandlerTests.cs index 89f29d355d5d..33d2ca4dc45c 100644 --- a/src/Controls/tests/Core.UnitTests/ShellUriHandlerTests.cs +++ b/src/Controls/tests/Core.UnitTests/ShellUriHandlerTests.cs @@ -307,7 +307,7 @@ public async Task ConvertToStandardFormat() if (!uri.IsAbsoluteUri) { - var reverse = new Uri(uri.OriginalString.Replace("/", "\\"), UriKind.Relative); + var reverse = new Uri(uri.OriginalString.Replace('/', '\\'), UriKind.Relative); Assert.AreEqual(new Uri("app://shell/IMPL_shell/path"), ShellUriHandler.ConvertToStandardFormat(shell, reverse)); } diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Bz44216.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Bz44216.xaml.cs index 78a6aa66e079..e034640651af 100644 --- a/src/Controls/tests/Xaml.UnitTests/Issues/Bz44216.xaml.cs +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Bz44216.xaml.cs @@ -38,7 +38,7 @@ class Tests public void DonSetValueOnPrivateBP(bool useCompiledXaml) { if (useCompiledXaml) - Assert.Throws(new BuildExceptionConstraint(7, 26, s => s.Contains("No property,")), () => MockCompiler.Compile(typeof(Bz44216))); + Assert.Throws(new BuildExceptionConstraint(7, 26, s => s.Contains("No property,", StringComparison.Ordinal)), () => MockCompiler.Compile(typeof(Bz44216))); else Assert.Throws(new XamlParseExceptionConstraint(7, 26, s => s.StartsWith("Cannot assign property", StringComparison.Ordinal)), () => new Bz44216(useCompiledXaml)); } diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Gh3539.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Gh3539.xaml.cs index 17d7c65755bf..40b067a08532 100644 --- a/src/Controls/tests/Xaml.UnitTests/Issues/Gh3539.xaml.cs +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Gh3539.xaml.cs @@ -117,7 +117,7 @@ private Gh3539NamedColor() public bool Equals(Gh3539NamedColor other) { - return Name.Equals(other.Name); + return Name.Equals(other.Name, StringComparison.Ordinal); } public int CompareTo(Gh3539NamedColor other) diff --git a/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs b/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs index fa35dde6f410..a40d72771740 100644 --- a/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs +++ b/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs @@ -299,7 +299,7 @@ public void ValidateOnly() public void ValidateOnly_WithErrors() { var project = NewProject(); - project.Add(AddFile("MainPage.xaml", "MauiXaml", Xaml.MainPage.Replace("", ""))); + project.Add(AddFile("MainPage.xaml", "MauiXaml", Xaml.MainPage.Replace("", "", StringComparison.Ordinal))); var projectFile = IOPath.Combine(tempDirectory, "test.csproj"); project.Save(projectFile); @@ -500,7 +500,7 @@ public void NoXamlFiles() var projectFile = IOPath.Combine(tempDirectory, "test.csproj"); project.Save(projectFile); var log = Build(projectFile, verbosity: "diagnostic"); - Assert.IsTrue(log.Contains("Target \"XamlC\" skipped"), "XamlC should be skipped if there are no .xaml files."); + Assert.IsTrue(log.Contains("Target \"XamlC\" skipped", StringComparison.Ordinal), "XamlC should be skipped if there are no .xaml files."); } } } diff --git a/src/Controls/tests/Xaml.UnitTests/Validation/TypeMismatch.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Validation/TypeMismatch.xaml.cs index f352df178597..8b9ebf7f6b57 100644 --- a/src/Controls/tests/Xaml.UnitTests/Validation/TypeMismatch.xaml.cs +++ b/src/Controls/tests/Xaml.UnitTests/Validation/TypeMismatch.xaml.cs @@ -21,7 +21,7 @@ public class Tests public void ThrowsOnMismatchingType([Values(true, false)] bool useCompiledXaml) { if (useCompiledXaml) - Assert.Throws(new BuildExceptionConstraint(7, 16, m => m.Contains("No property, BindableProperty")), () => MockCompiler.Compile(typeof(TypeMismatch))); + Assert.Throws(new BuildExceptionConstraint(7, 16, m => m.Contains("No property, BindableProperty", StringComparison.Ordinal)), () => MockCompiler.Compile(typeof(TypeMismatch))); else Assert.Throws(new XamlParseExceptionConstraint(7, 16, m => m.StartsWith("Cannot assign property", StringComparison.Ordinal)), () => new TypeMismatch(useCompiledXaml)); } diff --git a/src/Core/src/Converters/CornerRadiusTypeConverter.cs b/src/Core/src/Converters/CornerRadiusTypeConverter.cs index 6da68deb8f10..e41fb9952cc3 100644 --- a/src/Core/src/Converters/CornerRadiusTypeConverter.cs +++ b/src/Core/src/Converters/CornerRadiusTypeConverter.cs @@ -18,7 +18,7 @@ public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? if (strValue != null) { value = strValue.Trim(); - if (strValue.Contains(",")) + if (strValue.IndexOf(",", StringComparison.Ordinal) != -1) { //Xaml var cornerRadius = strValue.Split(','); if (cornerRadius.Length == 4 @@ -32,7 +32,7 @@ public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? && double.TryParse(cornerRadius[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double l)) return new CornerRadius(l); } - else if (strValue.Trim().Contains(" ")) + else if (strValue.Trim().IndexOf(" ", StringComparison.Ordinal) != -1) { //CSS var cornerRadius = strValue.Split(' '); if (cornerRadius.Length == 2 diff --git a/src/Core/src/Converters/ThicknessTypeConverter.cs b/src/Core/src/Converters/ThicknessTypeConverter.cs index 6e3f35f546ed..ae3fdb0616bd 100644 --- a/src/Core/src/Converters/ThicknessTypeConverter.cs +++ b/src/Core/src/Converters/ThicknessTypeConverter.cs @@ -20,7 +20,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c if (strValue != null) { strValue = strValue.Trim(); - if (strValue.Contains(",")) + if (strValue.IndexOf(",", StringComparison.Ordinal) != -1) { //Xaml var thickness = strValue.Split(','); switch (thickness.Length) @@ -39,7 +39,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c break; } } - else if (strValue.Contains(" ")) + else if (strValue.IndexOf(" ", StringComparison.Ordinal) != -1) { //CSS var thickness = strValue.Split(' '); switch (thickness.Length) diff --git a/src/Core/src/Fonts/FontFile.cs b/src/Core/src/Fonts/FontFile.cs index a22dd5f15b9f..c9dbf68eda07 100644 --- a/src/Core/src/Fonts/FontFile.cs +++ b/src/Core/src/Fonts/FontFile.cs @@ -33,9 +33,15 @@ public static FontFile FromString(string input) { _ = input ?? throw new ArgumentNullException(nameof(input)); - var hashIndex = input.IndexOf("#", global::System.StringComparison.Ordinal); + var hashIndex = input.IndexOf("#", StringComparison.Ordinal); //UWP names require Spaces. Sometimes people may use those, "CuteFont-Regular#Cute Font" should be "CuteFont-Regular#CuteFont" - var postScriptName = hashIndex > 0 ? input.Substring(hashIndex + 1).Replace(" ", "") : input; + var postScriptName = hashIndex > 0 ? input.Substring(hashIndex + 1) +#if NETSTANDARD2_0 + .Replace(" ", "") +#else + .Replace(" ", "", StringComparison.Ordinal) +#endif + : input; //Get the fontFamily name; var fontFamilyName = hashIndex > 0 ? input.Substring(0, hashIndex) : input; @@ -62,7 +68,7 @@ static IEnumerable GetFontName(string fontFamily) { _ = fontFamily ?? throw new ArgumentNullException(nameof(fontFamily)); - if (fontFamily.Contains(" ")) + if (fontFamily.IndexOf(" ", StringComparison.Ordinal) != -1) { yield return fontFamily; //We are done, they have spaces, they have it handled. diff --git a/src/Core/src/Fonts/FontManager.Android.cs b/src/Core/src/Fonts/FontManager.Android.cs index 16636d686192..5ca8e4362d97 100644 --- a/src/Core/src/Fonts/FontManager.Android.cs +++ b/src/Core/src/Fonts/FontManager.Android.cs @@ -158,7 +158,7 @@ string FontNameToFontFile(string fontFamily) { fontFamily ??= string.Empty; - int hashtagIndex = fontFamily.IndexOf('#'); + int hashtagIndex = fontFamily.IndexOf("#", StringComparison.Ordinal); if (hashtagIndex >= 0) return fontFamily.Substring(0, hashtagIndex); diff --git a/src/Core/src/Fonts/FontManager.Windows.cs b/src/Core/src/Fonts/FontManager.Windows.cs index 3dcd2c1fb302..dffb3ba8a3cd 100644 --- a/src/Core/src/Fonts/FontManager.Windows.cs +++ b/src/Core/src/Fonts/FontManager.Windows.cs @@ -64,7 +64,7 @@ IEnumerable GetAllFontPossibilities(string fontFamily) // First check Alias if (_fontRegistrar.GetFont(fontFamily) is string fontPostScriptName) { - if (fontPostScriptName!.Contains("://") && fontPostScriptName.Contains("#")) + if (fontPostScriptName.Contains("://", StringComparison.Ordinal) && fontPostScriptName.Contains("#", StringComparison.Ordinal)) { // The registrar has given us a perfect path, so use it exactly yield return fontPostScriptName; diff --git a/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs b/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs index 694f32fb6f7d..cc416083ea3d 100644 --- a/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs +++ b/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs @@ -373,7 +373,7 @@ async Task> GetCookiesFromPlatformStore(string url) { // we don't care that much about this being accurate // the cookie container will split the cookies up more correctly - if (!cookie.Domain.Contains(domain) && !domain.Contains(cookie.Domain)) + if (!cookie.Domain.Contains(domain, StringComparison.Ordinal) && !domain.Contains(cookie.Domain, StringComparison.Ordinal)) continue; existingCookies.Add(cookie); @@ -427,7 +427,7 @@ async Task DeleteCookies(List cookies) foreach (var deleteme in cookies) { - if (record.DisplayName.Contains(deleteme.Domain) || deleteme.Domain.Contains(record.DisplayName)) + if (record.DisplayName.Contains(deleteme.Domain, StringComparison.Ordinal) || deleteme.Domain.Contains(record.DisplayName, StringComparison.Ordinal)) { WKWebsiteDataStore.DefaultDataStore.RemoveDataOfTypes(record.DataTypes, new[] { record }, () => { }); diff --git a/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs b/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs index be3f56ea7b40..fa204cb94403 100644 --- a/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs +++ b/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs @@ -106,7 +106,7 @@ string GetFontSource(IFontImageSource imageSource) foreach (var family in allFamilies) { - if (family.Contains(source)) + if (family.Contains(source, StringComparison.Ordinal)) { fontSource = family; break; diff --git a/src/Core/src/Platform/Windows/MauiWebView.cs b/src/Core/src/Platform/Windows/MauiWebView.cs index 6b811c928944..d98218465edf 100644 --- a/src/Core/src/Platform/Windows/MauiWebView.cs +++ b/src/Core/src/Platform/Windows/MauiWebView.cs @@ -45,7 +45,7 @@ public async void LoadHtml(string? html, string? baseUrl) _internalWebView.NavigationCompleted += async (sender, args) => { // Generate a version of the script with the correct tag - var script = BaseInsertionScript.Replace("baseTag", baseTag); + var script = BaseInsertionScript.Replace("baseTag", baseTag, StringComparison.Ordinal); // Run it and retrieve the updated HTML from our WebView await sender.ExecuteScriptAsync(script); diff --git a/src/Core/src/Platform/Windows/TimePickerExtensions.cs b/src/Core/src/Platform/Windows/TimePickerExtensions.cs index 244bed3e4e7f..17575e508095 100644 --- a/src/Core/src/Platform/Windows/TimePickerExtensions.cs +++ b/src/Core/src/Platform/Windows/TimePickerExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Maui.Graphics; +using System; +using Microsoft.Maui.Graphics; using Microsoft.UI.Xaml.Controls; using WBrush = Microsoft.UI.Xaml.Media.Brush; @@ -10,7 +11,7 @@ public static void UpdateTime(this TimePicker nativeTimePicker, ITimePicker time { nativeTimePicker.Time = timePicker.Time; - if (timePicker.Format?.Contains('H') == true) + if (timePicker.Format?.Contains('H', StringComparison.Ordinal) == true) { nativeTimePicker.ClockIdentifier = "24HourClock"; } diff --git a/src/Core/src/Platform/iOS/TimePickerExtensions.cs b/src/Core/src/Platform/iOS/TimePickerExtensions.cs index d22c84642f98..d1c81c065412 100644 --- a/src/Core/src/Platform/iOS/TimePickerExtensions.cs +++ b/src/Core/src/Platform/iOS/TimePickerExtensions.cs @@ -43,21 +43,24 @@ public static void UpdateTime(this MauiTimePicker mauiTimePicker, ITimePicker ti mauiTimePicker.Text = time.ToFormattedString(format, cultureInfo); - if (timePicker.Format?.Contains('H') == true) + if (format != null) { - var ci = new CultureInfo("de-DE"); - NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName); + if (format.IndexOf("H", StringComparison.Ordinal) != -1) + { + var ci = new CultureInfo("de-DE"); + NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName); - if (picker != null) - picker.Locale = locale; - } - else if (timePicker.Format?.Contains('h') == true) - { - var ci = new CultureInfo("en-US"); - NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName); + if (picker != null) + picker.Locale = locale; + } + else if (format.IndexOf("h", StringComparison.Ordinal) != -1) + { + var ci = new CultureInfo("en-US"); + NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName); - if (picker != null) - picker.Locale = locale; + if (picker != null) + picker.Locale = locale; + } } mauiTimePicker.UpdateCharacterSpacing(timePicker); diff --git a/src/Core/src/Primitives/Font.cs b/src/Core/src/Primitives/Font.cs index f58e9e814289..5b943f65c871 100644 --- a/src/Core/src/Primitives/Font.cs +++ b/src/Core/src/Primitives/Font.cs @@ -91,7 +91,7 @@ public static Font SystemFontOfWeight(FontWeight weight, FontSlant fontSlant = F bool Equals(Font other) { - return string.Equals(Family, other.Family) + return string.Equals(Family, other.Family, StringComparison.Ordinal) && Size.Equals(other.Size) && Weight == other.Weight && Slant == other.Slant diff --git a/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.cs b/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.cs index 0f3087ef831a..759a3dc934fe 100644 --- a/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.cs +++ b/src/Core/tests/DeviceTests/Services/ImageSource/ImageSourceServiceTests.cs @@ -48,7 +48,7 @@ public void ThrowsWhenMissingService() var ex = Assert.Throws(() => provider.GetRequiredImageSourceService(new FileImageSourceStub())); - Assert.Contains(nameof(IFileImageSource), ex.Message); + Assert.Contains(nameof(IFileImageSource), ex.Message, StringComparison.Ordinal); } [Fact] @@ -58,8 +58,8 @@ public void ThrowsWhenNotASpecificImageSource() var ex = Assert.Throws(() => provider.GetRequiredImageSourceService(new InvalidImageSourceStub())); - Assert.Contains(nameof(InvalidImageSourceStub), ex.Message); - Assert.Contains(nameof(IImageSource), ex.Message); + Assert.Contains(nameof(InvalidImageSourceStub), ex.Message, StringComparison.Ordinal); + Assert.Contains(nameof(IImageSource), ex.Message, StringComparison.Ordinal); } [Fact] diff --git a/src/Core/tests/UnitTests/AbstractViewHandlerTests.cs b/src/Core/tests/UnitTests/AbstractViewHandlerTests.cs index 4c851fa9851b..7e1af49c00ee 100644 --- a/src/Core/tests/UnitTests/AbstractViewHandlerTests.cs +++ b/src/Core/tests/UnitTests/AbstractViewHandlerTests.cs @@ -51,8 +51,8 @@ public void GetRequiredServiceThrowsOnNoContext() var ex = Assert.Throws(() => handlerStub.GetRequiredService()); - Assert.Contains("the context", ex.Message); - Assert.Contains("MauiContext", ex.Message); + Assert.Contains("the context", ex.Message, StringComparison.Ordinal); + Assert.Contains("MauiContext", ex.Message, StringComparison.Ordinal); } [Fact] @@ -67,8 +67,8 @@ public void GetRequiredServiceThrowsOnNoServices() var ex = Assert.Throws(() => handlerStub.GetRequiredService()); - Assert.Contains("the service provider", ex.Message); - Assert.Contains("MauiContext", ex.Message); + Assert.Contains("the service provider", ex.Message, StringComparison.Ordinal); + Assert.Contains("MauiContext", ex.Message, StringComparison.Ordinal); } [Fact] diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderFontsTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderFontsTests.cs index ce5e915b568d..89a809e0bd8c 100644 --- a/src/Core/tests/UnitTests/Hosting/HostBuilderFontsTests.cs +++ b/src/Core/tests/UnitTests/Hosting/HostBuilderFontsTests.cs @@ -45,13 +45,13 @@ public void ConfigureFontsRegistersFonts(string filename, string alias) var path = registrar.GetFont(filename); Assert.NotNull(path); - Assert.StartsWith(root, path); + Assert.StartsWith(root, path, StringComparison.Ordinal); if (alias != null) { path = registrar.GetFont(alias); Assert.NotNull(path); - Assert.StartsWith(root, path); + Assert.StartsWith(root, path, StringComparison.Ordinal); } Assert.True(File.Exists(Path.Combine(root, filename))); diff --git a/src/Core/tests/UnitTests/Hosting/HostBuilderServicesTests.cs b/src/Core/tests/UnitTests/Hosting/HostBuilderServicesTests.cs index 26ab50946be0..b1b3bc7de1eb 100644 --- a/src/Core/tests/UnitTests/Hosting/HostBuilderServicesTests.cs +++ b/src/Core/tests/UnitTests/Hosting/HostBuilderServicesTests.cs @@ -37,7 +37,7 @@ public void GetServiceThrowsWhenNoPublicConstructors() var mauiApp = builder.Build(); var ex = Assert.Throws(() => mauiApp.Services.GetService()); - Assert.Contains("suitable constructor", ex.Message); + Assert.Contains("suitable constructor", ex.Message, StringComparison.Ordinal); } [Fact] diff --git a/src/Core/tests/UnitTests/PropertyMapperExtensionTests.cs b/src/Core/tests/UnitTests/PropertyMapperExtensionTests.cs index 4cabae38ac8d..3bf7f74becac 100644 --- a/src/Core/tests/UnitTests/PropertyMapperExtensionTests.cs +++ b/src/Core/tests/UnitTests/PropertyMapperExtensionTests.cs @@ -1,4 +1,5 @@ -using Microsoft.Maui.Controls; +using System; +using Microsoft.Maui.Controls; using Xunit; namespace Microsoft.Maui.UnitTests @@ -23,8 +24,8 @@ public void AddAfterMapping() mapper1.UpdateProperties(null, new Button()); - Assert.Contains(msg1, log); - Assert.Contains(msg2, log); + Assert.Contains(msg1, log, StringComparison.Ordinal); + Assert.Contains(msg2, log, StringComparison.Ordinal); var originalIndex = log.IndexOf(msg1); var additionalIndex = log.IndexOf(msg2); @@ -49,8 +50,8 @@ public void AddBeforeMapping() mapper1.UpdateProperties(null, new Button()); - Assert.Contains(msg1, log); - Assert.Contains(msg2, log); + Assert.Contains(msg1, log, StringComparison.Ordinal); + Assert.Contains(msg2, log, StringComparison.Ordinal); var originalIndex = log.IndexOf(msg1); var additionalIndex = log.IndexOf(msg2); @@ -75,8 +76,8 @@ public void ModifyMapping() mapper1.UpdateProperties(null, new Button()); - Assert.DoesNotContain(msg1, log); - Assert.Contains(msg2, log); + Assert.DoesNotContain(msg1, log, StringComparison.Ordinal); + Assert.Contains(msg2, log, StringComparison.Ordinal); } } } diff --git a/src/Essentials/samples/Samples/View/BasePage.cs b/src/Essentials/samples/Samples/View/BasePage.cs index b70b0a5f000c..2d043f16d7dd 100644 --- a/src/Essentials/samples/Samples/View/BasePage.cs +++ b/src/Essentials/samples/Samples/View/BasePage.cs @@ -57,7 +57,7 @@ Task OnDisplayAlert(string message) Task OnNavigate(BaseViewModel vm, bool showModal) { var name = vm.GetType().Name; - name = name.Replace("ViewModel", "Page"); + name = name.Replace("ViewModel", "Page", StringComparison.Ordinal); var ns = GetType().Namespace; var pageType = Type.GetType($"{ns}.{name}"); diff --git a/src/Essentials/samples/Samples/ViewModel/HomeViewModel.cs b/src/Essentials/samples/Samples/ViewModel/HomeViewModel.cs index 0a1e8ae25385..bee5927de0b5 100644 --- a/src/Essentials/samples/Samples/ViewModel/HomeViewModel.cs +++ b/src/Essentials/samples/Samples/ViewModel/HomeViewModel.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Maui.Essentials; @@ -285,13 +286,10 @@ static IEnumerable Filter(IEnumerable samples, string fi { if (!string.IsNullOrWhiteSpace(filterText)) { - var lower = filterText.ToLowerInvariant(); samples = samples.Where(s => { - var tags = s.Tags - .Union(new[] { s.Name }) - .Select(t => t.ToLowerInvariant()); - return tags.Any(t => t.Contains(lower)); + var tags = s.Tags.Union(new[] { s.Name }); + return tags.Any(t => t.Contains(filterText, StringComparison.OrdinalIgnoreCase)); }); } diff --git a/src/Essentials/samples/Samples/ViewModel/WebAuthenticatorViewModel.cs b/src/Essentials/samples/Samples/ViewModel/WebAuthenticatorViewModel.cs index 1c6fb3c5bc2b..6f96e9d4677c 100644 --- a/src/Essentials/samples/Samples/ViewModel/WebAuthenticatorViewModel.cs +++ b/src/Essentials/samples/Samples/ViewModel/WebAuthenticatorViewModel.cs @@ -41,7 +41,7 @@ async Task OnAuthenticate(string scheme) { WebAuthenticatorResult r = null; - if (scheme.Equals("Apple") + if (scheme.Equals("Apple", StringComparison.Ordinal) && DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.Version.Major >= 13) { diff --git a/src/Essentials/src/AppInfo/AppInfo.ios.tvos.watchos.macos.cs b/src/Essentials/src/AppInfo/AppInfo.ios.tvos.watchos.macos.cs index f4b01e94db04..d8a9c1f6e75c 100644 --- a/src/Essentials/src/AppInfo/AppInfo.ios.tvos.watchos.macos.cs +++ b/src/Essentials/src/AppInfo/AppInfo.ios.tvos.watchos.macos.cs @@ -113,9 +113,9 @@ public LayoutDirection RequestedLayoutDirection internal static bool VerifyHasUrlScheme(string scheme) { - var cleansed = scheme.Replace("://", string.Empty); + var cleansed = scheme.Replace("://", string.Empty, StringComparison.Ordinal); var schemes = GetCFBundleURLSchemes().ToList(); - return schemes.Any(x => x != null && x.Equals(cleansed, StringComparison.InvariantCultureIgnoreCase)); + return schemes.Any(x => x != null && x.Equals(cleansed, StringComparison.OrdinalIgnoreCase)); } internal static IEnumerable GetCFBundleURLSchemes() diff --git a/src/Essentials/src/Connectivity/Connectivity.android.cs b/src/Essentials/src/Connectivity/Connectivity.android.cs index 81d2eb689843..b824ccef1a3e 100644 --- a/src/Essentials/src/Connectivity/Connectivity.android.cs +++ b/src/Essentials/src/Connectivity/Connectivity.android.cs @@ -260,20 +260,19 @@ internal static ConnectionProfile GetConnectionType(ConnectivityType connectivit if (string.IsNullOrWhiteSpace(typeName)) return ConnectionProfile.Unknown; - var typeNameLower = typeName.ToLowerInvariant(); - if (typeNameLower.Contains("mobile")) + if (typeName.Contains("mobile", StringComparison.OrdinalIgnoreCase)) return ConnectionProfile.Cellular; - if (typeNameLower.Contains("wimax")) + if (typeName.Contains("wimax", StringComparison.OrdinalIgnoreCase)) return ConnectionProfile.Cellular; - if (typeNameLower.Contains("wifi")) + if (typeName.Contains("wifi", StringComparison.OrdinalIgnoreCase)) return ConnectionProfile.WiFi; - if (typeNameLower.Contains("ethernet")) + if (typeName.Contains("ethernet", StringComparison.OrdinalIgnoreCase)) return ConnectionProfile.Ethernet; - if (typeNameLower.Contains("bluetooth")) + if (typeName.Contains("bluetooth", StringComparison.OrdinalIgnoreCase)) return ConnectionProfile.Bluetooth; return ConnectionProfile.Unknown; diff --git a/src/Essentials/src/DeviceInfo/DeviceInfo.android.cs b/src/Essentials/src/DeviceInfo/DeviceInfo.android.cs index 4c322e90d26c..c4a1812b9dce 100644 --- a/src/Essentials/src/DeviceInfo/DeviceInfo.android.cs +++ b/src/Essentials/src/DeviceInfo/DeviceInfo.android.cs @@ -99,23 +99,23 @@ public DeviceType DeviceType get { var isEmulator = - (Build.Brand.StartsWith("generic", StringComparison.InvariantCulture) && Build.Device.StartsWith("generic", StringComparison.InvariantCulture)) || - Build.Fingerprint.StartsWith("generic", StringComparison.InvariantCulture) || - Build.Fingerprint.StartsWith("unknown", StringComparison.InvariantCulture) || - Build.Hardware.Contains("goldfish") || - Build.Hardware.Contains("ranchu") || - Build.Model.Contains("google_sdk") || - Build.Model.Contains("Emulator") || - Build.Model.Contains("Android SDK built for x86") || - Build.Manufacturer.Contains("Genymotion") || - Build.Manufacturer.Contains("VS Emulator") || - Build.Product.Contains("emulator") || - Build.Product.Contains("google_sdk") || - Build.Product.Contains("sdk") || - Build.Product.Contains("sdk_google") || - Build.Product.Contains("sdk_x86") || - Build.Product.Contains("simulator") || - Build.Product.Contains("vbox86p"); + (Build.Brand.StartsWith("generic", StringComparison.Ordinal) && Build.Device.StartsWith("generic", StringComparison.Ordinal)) || + Build.Fingerprint.StartsWith("generic", StringComparison.Ordinal) || + Build.Fingerprint.StartsWith("unknown", StringComparison.Ordinal) || + Build.Hardware.Contains("goldfish", StringComparison.Ordinal) || + Build.Hardware.Contains("ranchu", StringComparison.Ordinal) || + Build.Model.Contains("google_sdk", StringComparison.Ordinal) || + Build.Model.Contains("Emulator", StringComparison.Ordinal) || + Build.Model.Contains("Android SDK built for x86", StringComparison.Ordinal) || + Build.Manufacturer.Contains("Genymotion", StringComparison.Ordinal) || + Build.Manufacturer.Contains("VS Emulator", StringComparison.Ordinal) || + Build.Product.Contains("emulator", StringComparison.Ordinal) || + Build.Product.Contains("google_sdk", StringComparison.Ordinal) || + Build.Product.Contains("sdk", StringComparison.Ordinal) || + Build.Product.Contains("sdk_google", StringComparison.Ordinal) || + Build.Product.Contains("sdk_x86", StringComparison.Ordinal) || + Build.Product.Contains("simulator", StringComparison.Ordinal) || + Build.Product.Contains("vbox86p", StringComparison.Ordinal); if (isEmulator) return DeviceType.Virtual; diff --git a/src/Essentials/src/DeviceInfo/DeviceInfo.uwp.cs b/src/Essentials/src/DeviceInfo/DeviceInfo.uwp.cs index 6c7cbdbbb1ca..a018f2f77a2c 100644 --- a/src/Essentials/src/DeviceInfo/DeviceInfo.uwp.cs +++ b/src/Essentials/src/DeviceInfo/DeviceInfo.uwp.cs @@ -107,7 +107,7 @@ public DeviceType DeviceType if (string.IsNullOrWhiteSpace(systemProductName)) systemProductName = deviceInfo.SystemProductName; - var isVirtual = systemProductName.Contains("Virtual") || systemProductName == "HMV domU"; + var isVirtual = systemProductName.Contains("Virtual", StringComparison.Ordinal) || systemProductName == "HMV domU"; currentType = isVirtual ? DeviceType.Virtual : DeviceType.Physical; } diff --git a/src/Essentials/src/MediaPicker/MediaPicker.ios.cs b/src/Essentials/src/MediaPicker/MediaPicker.ios.cs index f37b6207cd18..03f8f40eb86e 100644 --- a/src/Essentials/src/MediaPicker/MediaPicker.ios.cs +++ b/src/Essentials/src/MediaPicker/MediaPicker.ios.cs @@ -119,7 +119,7 @@ static FileResult DictionaryToMediaFile(NSDictionary info) if (assetUrl != null) { - if (!assetUrl.Scheme.Equals("assets-library", StringComparison.InvariantCultureIgnoreCase)) + if (!assetUrl.Scheme.Equals("assets-library", StringComparison.OrdinalIgnoreCase)) return new UIDocumentFileResult(assetUrl); phAsset = info.ValueForKey(UIImagePickerController.PHAsset) as PHAsset; diff --git a/src/Essentials/src/PhoneDialer/PhoneDialer.android.cs b/src/Essentials/src/PhoneDialer/PhoneDialer.android.cs index 88c7a580244d..64c28a224025 100644 --- a/src/Essentials/src/PhoneDialer/PhoneDialer.android.cs +++ b/src/Essentials/src/PhoneDialer/PhoneDialer.android.cs @@ -1,3 +1,4 @@ +using System; using Android.App; using Android.Content; using Android.OS; @@ -32,7 +33,7 @@ public void Open(string number) phoneNumber = PhoneNumberUtils.FormatNumber(number, Java.Util.Locale.Default.Country) ?? phoneNumber; // if we are an extension then we need to encode - if (phoneNumber.Contains(',') || phoneNumber.Contains(';')) + if (phoneNumber.Contains(',', StringComparison.Ordinal) || phoneNumber.Contains(';', StringComparison.Ordinal)) phoneNumber = URLEncoder.Encode(phoneNumber, "UTF-8") ?? phoneNumber; var dialIntent = ResolveDialIntent(phoneNumber); diff --git a/src/Essentials/src/Preferences/Preferences.android.cs b/src/Essentials/src/Preferences/Preferences.android.cs index 81a58b641fd2..cef8c2ede4b7 100644 --- a/src/Essentials/src/Preferences/Preferences.android.cs +++ b/src/Essentials/src/Preferences/Preferences.android.cs @@ -121,7 +121,7 @@ public T Get(string key, T defaultValue, string sharedName) if (!double.TryParse(savedDouble, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out var outDouble)) { var maxString = Convert.ToString(double.MaxValue, CultureInfo.InvariantCulture); - outDouble = savedDouble.Equals(maxString) ? double.MaxValue : double.MinValue; + outDouble = savedDouble.Equals(maxString, StringComparison.Ordinal) ? double.MaxValue : double.MinValue; } value = outDouble; diff --git a/src/Essentials/src/Types/DeviceIdiom.shared.cs b/src/Essentials/src/Types/DeviceIdiom.shared.cs index d80bc7433f47..39ccc417a93d 100644 --- a/src/Essentials/src/Types/DeviceIdiom.shared.cs +++ b/src/Essentials/src/Types/DeviceIdiom.shared.cs @@ -53,7 +53,11 @@ public override bool Equals(object obj) => /// public override int GetHashCode() => - deviceIdiom == null ? 0 : deviceIdiom.GetHashCode(); + deviceIdiom == null ? 0 : deviceIdiom.GetHashCode( + #if !NETSTANDARD2_0 + StringComparison.Ordinal + #endif + ); /// public override string ToString() => diff --git a/src/Essentials/src/Types/DevicePlatform.shared.cs b/src/Essentials/src/Types/DevicePlatform.shared.cs index c59581374c09..f8c56d8cd6ec 100644 --- a/src/Essentials/src/Types/DevicePlatform.shared.cs +++ b/src/Essentials/src/Types/DevicePlatform.shared.cs @@ -61,7 +61,11 @@ public override bool Equals(object obj) => /// public override int GetHashCode() => - devicePlatform == null ? 0 : devicePlatform.GetHashCode(); + devicePlatform == null ? 0 : devicePlatform.GetHashCode( + #if !NETSTANDARD2_0 + StringComparison.Ordinal + #endif + ); /// public override string ToString() => diff --git a/src/Essentials/src/Types/Shared/WebUtils.shared.cs b/src/Essentials/src/Types/Shared/WebUtils.shared.cs index f7ba3b97bcf5..b0f8eb9d0bde 100644 --- a/src/Essentials/src/Types/Shared/WebUtils.shared.cs +++ b/src/Essentials/src/Types/Shared/WebUtils.shared.cs @@ -12,12 +12,12 @@ internal static IDictionary ParseQueryString(string url) { var d = new Dictionary(); - if (string.IsNullOrWhiteSpace(url) || (!url.Contains("?") && !url.Contains("#"))) + if (string.IsNullOrWhiteSpace(url) || (url.IndexOf("?", StringComparison.Ordinal) == -1 && url.IndexOf("#", StringComparison.Ordinal) == -1)) return d; - var qsStartIndex = url.IndexOf('?'); + var qsStartIndex = url.IndexOf("?", StringComparison.Ordinal); if (qsStartIndex < 0) - qsStartIndex = url.IndexOf('#'); + qsStartIndex = url.IndexOf("#", StringComparison.Ordinal); if (url.Length - 1 < qsStartIndex + 1) return d; diff --git a/src/Essentials/test/DeviceTests/Tests/Android/FileProvider_Tests.cs b/src/Essentials/test/DeviceTests/Tests/Android/FileProvider_Tests.cs index fad5007f9304..8a2190449445 100644 --- a/src/Essentials/test/DeviceTests/Tests/Android/FileProvider_Tests.cs +++ b/src/Essentials/test/DeviceTests/Tests/Android/FileProvider_Tests.cs @@ -75,7 +75,7 @@ public void Get_Shareable_Uri(bool failAccess, FileProviderLocation location) // Make sure the underlying file exists var realPath = Path.Combine(shareableUri.PathSegments.ToArray()) - .Replace(expectedCache, expectedCacheDir); + .Replace(expectedCache, expectedCacheDir, StringComparison.Ordinal); Assert.True(File.Exists(realPath)); } finally @@ -133,7 +133,7 @@ public void Get_External_Cache_Shareable_Uri() // Make sure the underlying file exists var realPath = Path.Combine(shareableUri.PathSegments.ToArray()) - .Replace("external_cache", Platform.AppContext.ExternalCacheDir.AbsolutePath); + .Replace("external_cache", Platform.AppContext.ExternalCacheDir.AbsolutePath, StringComparison.Ordinal); Assert.True(File.Exists(realPath)); } @@ -212,7 +212,7 @@ public void Get_Existing_External_Shareable_Uri(FileProviderLocation location) #pragma warning restore CS0618 // Type or member is obsolete // replace the real root with the providers "root" - var segements = Path.Combine(root.Replace(externalRoot, "external_files"), Path.GetFileName(file)); + var segements = Path.Combine(root.Replace(externalRoot, "external_files", StringComparison.Ordinal), Path.GetFileName(file)); Assert.Equal(segements.Split(Path.DirectorySeparatorChar), shareableUri.PathSegments); } diff --git a/src/Essentials/test/DeviceTests/Tests/DeviceInfo_Tests.cs b/src/Essentials/test/DeviceTests/Tests/DeviceInfo_Tests.cs index e344bff287e7..8718e7ed77cb 100644 --- a/src/Essentials/test/DeviceTests/Tests/DeviceInfo_Tests.cs +++ b/src/Essentials/test/DeviceTests/Tests/DeviceInfo_Tests.cs @@ -13,7 +13,7 @@ public void Versions_Are_Correct() #if WINDOWS_UWP || WINDOWS Assert.Equal(10, DeviceInfo.Version.Major); Assert.Equal(0, DeviceInfo.Version.Minor); - Assert.StartsWith("10.0", DeviceInfo.VersionString); + Assert.StartsWith("10.0", DeviceInfo.VersionString, StringComparison.Ordinal); #else Assert.True(DeviceInfo.Version.Major > 0); #endif @@ -40,10 +40,10 @@ public void DeviceModel_Is_Correct() if (DeviceInfo.DeviceType == DeviceType.Virtual) { var isEmulator = - DeviceInfo.Model.Contains("sdk_gphone_x86") || - DeviceInfo.Model.Contains("google_sdk") || - DeviceInfo.Model.Contains("Emulator") || - DeviceInfo.Model.Contains("Android SDK built for x86"); + DeviceInfo.Model.Contains("sdk_gphone_x86", StringComparison.Ordinal) || + DeviceInfo.Model.Contains("google_sdk", StringComparison.Ordinal) || + DeviceInfo.Model.Contains("Emulator", StringComparison.Ordinal) || + DeviceInfo.Model.Contains("Android SDK built for x86", StringComparison.Ordinal); Assert.True(isEmulator); } diff --git a/src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs b/src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs index da664111ecdf..537215a695e7 100644 --- a/src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs +++ b/src/Essentials/test/DeviceTests/Tests/Geocoding_Tests.cs @@ -64,6 +64,6 @@ public async Task Get_Locations(string address) } static bool IsEmulatorFailure(Exception ex) => - ex.Message.ToLower().Contains("grpc") || ex.Message.ToLower().Contains("service not available"); + ex.Message.Contains("grpc", StringComparison.OrdinalIgnoreCase) || ex.Message.Contains("service not available", StringComparison.OrdinalIgnoreCase); } } diff --git a/src/Essentials/test/DeviceTests/Tests/Launcher_Tests.cs b/src/Essentials/test/DeviceTests/Tests/Launcher_Tests.cs index 76996249af08..57515cecfaa2 100644 --- a/src/Essentials/test/DeviceTests/Tests/Launcher_Tests.cs +++ b/src/Essentials/test/DeviceTests/Tests/Launcher_Tests.cs @@ -36,7 +36,7 @@ public Task Open(string uri) public async Task CanOpen(string uri) { #if __IOS__ - if (DeviceInfo.DeviceType == DeviceType.Virtual && (uri.Contains("tel:") || uri.Contains("mailto:"))) + if (DeviceInfo.DeviceType == DeviceType.Virtual && (uri.Contains("tel:", StringComparison.Ordinal) || uri.Contains("mailto:", StringComparison.Ordinal))) { Assert.False(await Launcher.CanOpenAsync(uri)); return; @@ -57,7 +57,7 @@ public async Task CanOpen(string uri) public async Task CanOpenUri(string uri) { #if __IOS__ - if (DeviceInfo.DeviceType == DeviceType.Virtual && (uri.Contains("tel:") || uri.Contains("mailto:"))) + if (DeviceInfo.DeviceType == DeviceType.Virtual && (uri.Contains("tel:", StringComparison.Ordinal) || uri.Contains("mailto:", StringComparison.Ordinal))) { Assert.False(await Launcher.CanOpenAsync(new Uri(uri))); return; @@ -112,7 +112,7 @@ public async Task CanNotOpen(string uri) public async Task TryOpen(string uri) { #if __IOS__ - if (DeviceInfo.DeviceType == DeviceType.Virtual && (uri.Contains("tel:") || uri.Contains("mailto:"))) + if (DeviceInfo.DeviceType == DeviceType.Virtual && (uri.Contains("tel:", StringComparison.Ordinal) || uri.Contains("mailto:", StringComparison.Ordinal))) { Assert.False(await Launcher.TryOpenAsync(uri)); return; diff --git a/src/SingleProject/Resizetizer/test/UnitTests/DetectInvalidResourceOutputFilenamesTests.cs b/src/SingleProject/Resizetizer/test/UnitTests/DetectInvalidResourceOutputFilenamesTests.cs index af89d66e0b5c..928b63cb3aee 100644 --- a/src/SingleProject/Resizetizer/test/UnitTests/DetectInvalidResourceOutputFilenamesTests.cs +++ b/src/SingleProject/Resizetizer/test/UnitTests/DetectInvalidResourceOutputFilenamesTests.cs @@ -24,7 +24,7 @@ protected DetectInvalidResourceOutputFilenamesTask GetNewTask(params ITaskItem[] }; protected string GetInvalidFilename(DetectInvalidResourceOutputFilenamesTask task, string path) => - task.InvalidItems.Single(c => c.Replace("\\", "/").EndsWith(path)); + task.InvalidItems.Single(c => c.Replace('\\', '/').EndsWith(path, StringComparison.Ordinal)); protected void AssertValidFilename(DetectInvalidResourceOutputFilenamesTask task, ITaskItem item) => Assert.DoesNotContain(task.InvalidItems ?? Enumerable.Empty(), c => c == item.ItemSpec); diff --git a/src/SingleProject/Resizetizer/test/UnitTests/ResizetizeImagesTests.cs b/src/SingleProject/Resizetizer/test/UnitTests/ResizetizeImagesTests.cs index 6790a577a70f..ad2467f85699 100644 --- a/src/SingleProject/Resizetizer/test/UnitTests/ResizetizeImagesTests.cs +++ b/src/SingleProject/Resizetizer/test/UnitTests/ResizetizeImagesTests.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -28,7 +29,7 @@ protected ResizetizeImages GetNewTask(string type, params ITaskItem[] items) => }; protected ITaskItem GetCopiedResource(ResizetizeImages task, string path) => - task.CopiedResources.Single(c => c.ItemSpec.Replace("\\", "/").EndsWith(path)); + task.CopiedResources.Single(c => c.ItemSpec.Replace('\\', '/').EndsWith(path, StringComparison.Ordinal)); protected void AssertFileSize(string file, int width, int height) { @@ -62,7 +63,7 @@ protected void AssertFileContains(string file, params string[] snippet) var content = File.ReadAllText(file); foreach (var snip in snippet) - Assert.Contains(snip, content); + Assert.Contains(snip, content, StringComparison.Ordinal); } } diff --git a/src/TestUtils/src/DeviceTests.Runners/VisualRunner/Sinks/DeviceExecutionSink.cs b/src/TestUtils/src/DeviceTests.Runners/VisualRunner/Sinks/DeviceExecutionSink.cs index da68e9e20108..d94ad289d9b8 100644 --- a/src/TestUtils/src/DeviceTests.Runners/VisualRunner/Sinks/DeviceExecutionSink.cs +++ b/src/TestUtils/src/DeviceTests.Runners/VisualRunner/Sinks/DeviceExecutionSink.cs @@ -51,7 +51,7 @@ async void MakeTestResultViewModel(ITestResultMessage testResult, TestState outc if (!_testCases.TryGetValue(testResult.TestCase, out TestCaseViewModel? testCase)) { // no matching reference, search by Unique ID as a fallback - testCase = _testCases.FirstOrDefault(kvp => kvp.Key.UniqueID?.Equals(testResult.TestCase.UniqueID) ?? false).Value; + testCase = _testCases.FirstOrDefault(kvp => kvp.Key.UniqueID?.Equals(testResult.TestCase.UniqueID, StringComparison.Ordinal) ?? false).Value; if (testCase == null) return; diff --git a/src/TestUtils/src/DeviceTests.Runners/VisualRunner/Utils/TestRunLogger.cs b/src/TestUtils/src/DeviceTests.Runners/VisualRunner/Utils/TestRunLogger.cs index a1528b23c358..228aaffeb6e0 100644 --- a/src/TestUtils/src/DeviceTests.Runners/VisualRunner/Utils/TestRunLogger.cs +++ b/src/TestUtils/src/DeviceTests.Runners/VisualRunner/Utils/TestRunLogger.cs @@ -54,7 +54,7 @@ public void LogTestResult(TestResultViewModel result) if (!string.IsNullOrEmpty(message)) { _builder.Append(" : "); - _builder.Append(message.Replace("\r", "\\r").Replace("\n", "\\n")); + _builder.Append(message.Replace("\r", "\\r", StringComparison.Ordinal).Replace("\n", "\\n", StringComparison.Ordinal)); } _builder.AppendLine(); diff --git a/src/TestUtils/src/DeviceTests.Runners/VisualRunner/XamlExtensions/EmbeddedResourceExtension.cs b/src/TestUtils/src/DeviceTests.Runners/VisualRunner/XamlExtensions/EmbeddedResourceExtension.cs index 47867ede2af9..240433959c9f 100644 --- a/src/TestUtils/src/DeviceTests.Runners/VisualRunner/XamlExtensions/EmbeddedResourceExtension.cs +++ b/src/TestUtils/src/DeviceTests.Runners/VisualRunner/XamlExtensions/EmbeddedResourceExtension.cs @@ -15,7 +15,7 @@ class EmbeddedResourceExtension : IMarkupExtension if (Name == null) return null; - var resourceName = "." + Name.Trim().Replace("/", ".").Replace("\\", "."); + var resourceName = "." + Name.Trim().Replace('/', '.').Replace('\\', '.'); var assembly = typeof(MauiVisualRunnerApp).Assembly; foreach (var name in assembly.GetManifestResourceNames())