From 501f7c7c339491fddf7425f2193695f703dfb04f Mon Sep 17 00:00:00 2001 From: Jonas Nyrup Date: Sun, 18 Aug 2024 15:44:59 +0200 Subject: [PATCH] Apply the Type-Dictionary Trick From https://mariusgundersen.net/type-dictionary-trick/ --- .../BrowserContextOverridePermissionsTests.cs | 2 +- lib/PuppeteerSharp/BrowserData/Cache.cs | 7 +- lib/PuppeteerSharp/BrowserData/Firefox.cs | 2 +- lib/PuppeteerSharp/Helpers/EnumHelper.cs | 120 ++++++++++-------- 4 files changed, 71 insertions(+), 60 deletions(-) diff --git a/lib/PuppeteerSharp.Tests/BrowserContextTests/BrowserContextOverridePermissionsTests.cs b/lib/PuppeteerSharp.Tests/BrowserContextTests/BrowserContextOverridePermissionsTests.cs index da1cee628..900772630 100644 --- a/lib/PuppeteerSharp.Tests/BrowserContextTests/BrowserContextOverridePermissionsTests.cs +++ b/lib/PuppeteerSharp.Tests/BrowserContextTests/BrowserContextOverridePermissionsTests.cs @@ -111,7 +111,7 @@ public async Task AllEnumsdAreValid() await Page.GoToAsync(TestConstants.EmptyPage); await Context.OverridePermissionsAsync( TestConstants.EmptyPage, - Enum.GetValues(typeof(OverridePermission)).Cast().ToArray()); + Enum.GetValues()); Assert.That(await GetPermissionAsync(Page, "geolocation"), Is.EqualTo("granted")); await Context.ClearPermissionOverridesAsync(); Assert.That(await GetPermissionAsync(Page, "geolocation"), Is.EqualTo("prompt")); diff --git a/lib/PuppeteerSharp/BrowserData/Cache.cs b/lib/PuppeteerSharp/BrowserData/Cache.cs index f168e7b64..e00f000cb 100644 --- a/lib/PuppeteerSharp/BrowserData/Cache.cs +++ b/lib/PuppeteerSharp/BrowserData/Cache.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using PuppeteerSharp.Helpers; namespace PuppeteerSharp.BrowserData { @@ -27,12 +28,12 @@ public IEnumerable GetInstalledBrowsers() return Array.Empty(); } - var browserNames = Enum.GetNames(typeof(SupportedBrowser)).Select(browser => browser.ToUpperInvariant()); + var browserNames = EnumHelper.GetNames().Select(browser => browser.ToUpperInvariant()); var browsers = rootInfo.GetDirectories().Where(browser => browserNames.Contains(browser.Name.ToUpperInvariant())); return browsers.SelectMany(browser => { - var browserEnum = (SupportedBrowser)Enum.Parse(typeof(SupportedBrowser), browser.Name, ignoreCase: true); + var browserEnum = EnumHelper.Parse(browser.Name, ignoreCase: true); var dirInfo = new DirectoryInfo(GetBrowserRoot(browserEnum)); var dirs = dirInfo.GetDirectories(); @@ -45,7 +46,7 @@ public IEnumerable GetInstalledBrowsers() return null; } - var platformEnum = (Platform)Enum.Parse(typeof(Platform), result.Value.Platform, ignoreCase: true); + var platformEnum = EnumHelper.Parse(result.Value.Platform, ignoreCase: true); return new InstalledBrowser(this, browserEnum, result.Value.BuildId, platformEnum); }) .Where(item => item != null); diff --git a/lib/PuppeteerSharp/BrowserData/Firefox.cs b/lib/PuppeteerSharp/BrowserData/Firefox.cs index bcf54eb29..88baadc5b 100644 --- a/lib/PuppeteerSharp/BrowserData/Firefox.cs +++ b/lib/PuppeteerSharp/BrowserData/Firefox.cs @@ -131,7 +131,7 @@ internal static void CreateProfile(string tempUserDataDirectory, Dictionary().Select(v => v.ToValueString())) + foreach (var value in EnumHelper.GetValues().Select(v => v.ToValueString())) { if (buildId.StartsWith(value, StringComparison.OrdinalIgnoreCase)) { diff --git a/lib/PuppeteerSharp/Helpers/EnumHelper.cs b/lib/PuppeteerSharp/Helpers/EnumHelper.cs index 1d1c4104f..59f8b7c8b 100644 --- a/lib/PuppeteerSharp/Helpers/EnumHelper.cs +++ b/lib/PuppeteerSharp/Helpers/EnumHelper.cs @@ -21,7 +21,6 @@ // * SOFTWARE. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; @@ -31,66 +30,69 @@ namespace PuppeteerSharp.Helpers; internal static class EnumHelper { - private static readonly ConcurrentDictionary> _stringToEnumCache = new(); + public static string[] GetNames() + where TEnum : struct, Enum => +#if NET8_0_OR_GREATER + Enum.GetNames(); +#else + Enum.GetNames(typeof(TEnum)); +#endif - private static readonly ConcurrentDictionary> _enumToStringCache = new(); + public static TEnum[] GetValues() + where TEnum : struct, Enum => +#if NET8_0_OR_GREATER + Enum.GetValues(); +#else + (TEnum[])Enum.GetValues(typeof(TEnum)); +#endif - public static TEnum ToEnum(this string value) - where TEnum : Enum - { - var enumValues = _stringToEnumCache.GetOrAdd(typeof(TEnum), type => - { - var names = Enum.GetNames(type); - var values = (TEnum[])Enum.GetValues(type); - var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - for (var i = 0; i < names.Length; i++) - { - dictionary.Add(names[i], values[i]); - var memberValue = type.GetField(names[i]).GetCustomAttribute()?.Value; - if (memberValue != null) - { - dictionary[memberValue] = values[i]; - } - } + public static TEnum Parse(string value, bool ignoreCase) + where TEnum : struct, Enum => +#if NET8_0_OR_GREATER + Enum.Parse(value, ignoreCase); +#else + (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase); +#endif - return dictionary; - }); - return (TEnum)enumValues[value]; - } + public static TEnum ToEnum(this string value) + where TEnum : struct, Enum + => StringToEnum.Cache[value]; public static string ToValueString(this TEnum value) - where TEnum : Enum + where TEnum : struct, Enum + => EnumToString.Cache[value]; + + public static TEnum FromValueString(string value) + where TEnum : struct, Enum { - var enumValues = _enumToStringCache.GetOrAdd(typeof(TEnum), type => + if (StringToEnum.Cache.TryGetValue(value, out var enumValue)) { - var names = Enum.GetNames(type); - var dictionary = new Dictionary(); - foreach (var t in names) - { - var field = type.GetField(t); - var valueName = field.GetCustomAttribute()?.Value ?? t; - var fieldValue = (TEnum)field.GetValue(null); - dictionary[fieldValue] = valueName; - } + return enumValue; + } - return dictionary; - }); + var defaultEnumAttribute = typeof(TEnum).GetCustomAttribute(); + if (defaultEnumAttribute != null) + { + return (TEnum)(object)defaultEnumAttribute.Value; + } - return enumValues[value]; + throw new ArgumentException($"Unknown value '{value}' for enum {typeof(TEnum).Name}"); } - public static TEnum FromValueString(string value) + private static class StringToEnum where TEnum : struct, Enum { - var enumValues = _stringToEnumCache.GetOrAdd(typeof(TEnum), type => + public static readonly IReadOnlyDictionary Cache = Compute(); + + private static Dictionary Compute() { - var names = Enum.GetNames(type); - var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var valueName in names) + var names = GetNames(); + var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var name in names) { - var field = type.GetField(valueName); + var field = typeof(TEnum).GetField(name); var fieldValue = (TEnum)field.GetValue(null); - dictionary[valueName] = fieldValue; + dictionary[name] = fieldValue; if (field.GetCustomAttribute()?.Value is { } enumMember) { dictionary[enumMember] = fieldValue; @@ -98,19 +100,27 @@ public static TEnum FromValueString(string value) } return dictionary; - }); - - if (enumValues.TryGetValue(value, out var enumValue)) - { - return (TEnum)enumValue; } + } - var defaultEnumAttribute = typeof(TEnum).GetCustomAttribute(); - if (defaultEnumAttribute != null) + private static class EnumToString + where TEnum : struct, Enum + { + public static readonly IReadOnlyDictionary Cache = Compute(); + + private static Dictionary Compute() { - return (TEnum)(object)defaultEnumAttribute.Value; - } + var names = GetNames(); + var dictionary = new Dictionary(); + foreach (var name in names) + { + var field = typeof(TEnum).GetField(name); + var valueName = field.GetCustomAttribute()?.Value ?? name; + var fieldValue = (TEnum)field.GetValue(null); + dictionary[fieldValue] = valueName; + } - throw new ArgumentException($"Unknown value '{value}' for enum {typeof(TEnum).Name}"); + return dictionary; + } } }