Skip to content

Commit 9e05ed7

Browse files
Initial extensions checkin.
1 parent 31eb887 commit 9e05ed7

6 files changed

Lines changed: 179 additions & 23 deletions

File tree

Benchmarks/EnumParseTests.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using BenchmarkDotNet.Attributes;
2+
using FastEnumUtility;
3+
4+
namespace Open.Text.Benchmarks
5+
{
6+
7+
public enum Greek
8+
{
9+
Alpha, Beta, Cappa, Delta, Epsilon, Gamma, Omega, Phi, Theta, None
10+
}
11+
12+
[MemoryDiagnoser]
13+
public class EnumParseTests
14+
{
15+
[Params(true, false)]
16+
public bool UseValid { get; set; }
17+
18+
[Params(false, true)]
19+
public bool IgnoreCase { get; set; }
20+
21+
22+
static readonly string[] ValidValues = new string[] { Greek.Alpha.ToString(), Greek.Epsilon.ToString(), Greek.Phi.ToString() };
23+
static readonly string[] InvalidValues = new string[] { "Apple", "Orange", "Pineapple" };
24+
25+
[Benchmark(Baseline = true)]
26+
public Greek EnumParse()
27+
{
28+
Greek e = Greek.None;
29+
if (UseValid)
30+
{
31+
foreach (string s in ValidValues)
32+
{
33+
if (!Enum.TryParse(s, IgnoreCase, out e))
34+
throw new Exception("Invalid.");
35+
}
36+
}
37+
else
38+
{
39+
foreach (string s in InvalidValues)
40+
{
41+
if (Enum.TryParse(s, IgnoreCase, out e))
42+
throw new Exception("Valid.");
43+
}
44+
45+
}
46+
return e;
47+
}
48+
49+
[Benchmark]
50+
public Greek EnumValueParse()
51+
{
52+
Greek e = Greek.None;
53+
if (UseValid)
54+
{
55+
foreach (string s in ValidValues)
56+
{
57+
if (!EnumValue.TryParse(s, IgnoreCase, out e))
58+
throw new Exception("Invalid.");
59+
}
60+
}
61+
else
62+
{
63+
foreach (string s in InvalidValues)
64+
{
65+
if (EnumValue.TryParse(s, IgnoreCase, out e))
66+
throw new Exception("Valid.");
67+
}
68+
69+
}
70+
return e;
71+
}
72+
73+
74+
[Benchmark]
75+
public Greek FastEnumParse()
76+
{
77+
Greek e = Greek.None;
78+
if (UseValid)
79+
{
80+
foreach (string s in ValidValues)
81+
{
82+
if (!FastEnum.TryParse(s, IgnoreCase, out e))
83+
throw new Exception("Invalid.");
84+
}
85+
}
86+
else
87+
{
88+
foreach (string s in InvalidValues)
89+
{
90+
if (FastEnum.TryParse(s, IgnoreCase, out e))
91+
throw new Exception("Valid.");
92+
}
93+
94+
}
95+
return e;
96+
}
97+
}
98+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
12+
<PackageReference Include="FastEnum" Version="1.6.1" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\Source\Open.Text.csproj" />
17+
</ItemGroup>
18+
19+
</Project>

Benchmarks/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
using Open.Text.Benchmarks;
2+
using BenchmarkDotNet.Running;
3+
4+
BenchmarkRunner.Run<EnumParseTests>();

Open.Text.sln

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 16
4-
VisualStudioVersion = 16.0.30204.135
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31710.8
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Text", "Source\Open.Text.csproj", "{3C245F69-844A-4DC9-93E7-C02545B013C0}"
77
EndProject
88
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Text.Tests", "Tests\Open.Text.Tests.csproj", "{C5BB368A-90CC-4DDA-B587-DAE590308F08}"
99
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Text.Benchmarks", "Benchmarks\Open.Text.Benchmarks.csproj", "{CD6319A3-D46A-4764-B4C1-F4C31CCA0EB0}"
11+
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1214
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
2123
{C5BB368A-90CC-4DDA-B587-DAE590308F08}.Debug|Any CPU.Build.0 = Debug|Any CPU
2224
{C5BB368A-90CC-4DDA-B587-DAE590308F08}.Release|Any CPU.ActiveCfg = Release|Any CPU
2325
{C5BB368A-90CC-4DDA-B587-DAE590308F08}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{CD6319A3-D46A-4764-B4C1-F4C31CCA0EB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{CD6319A3-D46A-4764-B4C1-F4C31CCA0EB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{CD6319A3-D46A-4764-B4C1-F4C31CCA0EB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{CD6319A3-D46A-4764-B4C1-F4C31CCA0EB0}.Release|Any CPU.Build.0 = Release|Any CPU
2430
EndGlobalSection
2531
GlobalSection(SolutionProperties) = preSolution
2632
HideSolutionNode = FALSE

Source/EnumValue.cs

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ public EnumValue(string value)
4646
static readonly Func<string, TEnum> Parser = CreateParseEnumDelegate();
4747

4848
/// <summary>
49-
/// Uses a case-insenstive dictionary lookup to get a matching enum value.
49+
/// Uses an expression tree switch to get a matching enum value.
5050
/// </summary>
51-
/// <returns>The enum the represents the string <paramref name="value"/> provided.</returns>
51+
/// <param name="value">The string represnting the enum to search for.</param>
52+
/// <returns>The enum that represents the string <paramref name="value"/> provided.</returns>
5253
/// <exception cref="ArgumentNullException">value is null</exception>
5354
/// <exception cref="ArgumentException">Requested <paramref name="value"/> was not found.</exception>
5455
public static TEnum Parse(string value)
@@ -67,7 +68,7 @@ public static TEnum Parse(string value)
6768
}
6869

6970
/// <summary>
70-
/// Uses a case-insenstive dictionary lookup to get a matching enum value.
71+
/// Uses an expression tree switch to get a matching enum value.
7172
/// </summary>
7273
/// <returns>true if the value found; otherwise false.</returns>
7374
/// <exception cref="ArgumentNullException"/>
@@ -126,7 +127,7 @@ static Func<string, TEnum> CreateParseEnumDelegate()
126127
/// Indicates whether this instance matches the provided enum <paramref name="value"/>.
127128
/// </summary>
128129
/// <returns>true if <paramref name="value"/> and this instance's enum value are the same; otherwise false.</returns>
129-
public bool Equals(TEnum value) => Value.Equals(value);
130+
public bool Equals(TEnum value) => Value.Equals(value);
130131
public static bool operator ==(EnumValue<TEnum> left, TEnum right) => left.Value.Equals(right);
131132
public static bool operator !=(EnumValue<TEnum> left, TEnum right) => !left.Value.Equals(right);
132133

@@ -172,7 +173,7 @@ public EnumValueCaseIgnored(TEnum value)
172173
/// <summary>
173174
/// Parses the string value to construct an EnumValueCaseIgnored&lt;<typeparamref name="TEnum"/>&gt; instance.
174175
/// </summary>
175-
/// <exception cref="ArgumentNullException">value is null.</exception>
176+
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
176177
public EnumValueCaseIgnored(string value)
177178
{
178179
Value = Parse(value);
@@ -186,30 +187,21 @@ public EnumValueCaseIgnored(string value)
186187
internal static readonly ImmutableDictionary<string, TEnum> CaseInsensitiveLookup
187188
= CreateCaseInsensitiveDictionary();
188189

189-
/// <inheritdoc cref="EnumValue{TEnum}.Parse(string)" />
190190
/// <summary>
191-
/// Uses an expression tree switch to get a matching enum value.
191+
/// Uses a case-insenstive dictionary lookup to get a matching enum value.
192192
/// </summary>
193+
/// <inheritdoc cref="EnumValue{TEnum}.Parse(string)" />
193194
[MethodImpl(MethodImplOptions.AggressiveInlining)]
194195
public static TEnum Parse(string value)
195-
{
196-
try
197-
{
198-
return CaseInsensitiveLookup[value];
199-
}
200-
catch (KeyNotFoundException ex)
201-
{
202-
throw new ArgumentException($"Requested value '{value}' was not found.", nameof(value), ex);
203-
}
204-
}
196+
=> TryParse(value, out var e) ? e : throw new ArgumentException($"Requested value '{value}' was not found.", nameof(value));
205197

206-
/// <inheritdoc cref="EnumValue{TEnum}.TryParse(string, out TEnum)"/>
207198
/// <summary>
208-
/// Uses an expression tree switch to get a matching enum value.
199+
/// Uses a case-insenstive dictionary lookup to get a matching enum value.
209200
/// </summary>
201+
/// <inheritdoc cref="EnumValue{TEnum}.TryParse(string, out TEnum)"/>
210202
public static bool TryParse(string value, out TEnum e)
211203
{
212-
if(CaseInsensitiveLookup.TryGetValue(value, out e!)) return true;
204+
if (CaseInsensitiveLookup.TryGetValue(value, out e!)) return true;
213205
e = default!;
214206
return false;
215207
}
@@ -258,4 +250,41 @@ private string GetDebuggerDisplay()
258250
return $"{eType.Name}.{Value} [EnumValueCaseIgnored<{eType.FullName}>]";
259251
}
260252
}
253+
254+
public static class EnumValue
255+
{
256+
/// <inheritdoc cref="EnumValue{TEnum}.Parse(string)"/>
257+
public static TEnum Parse<TEnum>(string value)
258+
where TEnum : Enum
259+
=> EnumValue<TEnum>.Parse(value);
260+
261+
/// <summary>
262+
/// Converts the string representation of the name of one or more enumerated constants to an equivalent enumerated object.
263+
/// A parameter specifies whether the operation is case-insensitive.
264+
/// </summary>
265+
/// <param name="ignoreCase">If true, will ignore case differences when looking for a match.</param>
266+
/// <inheritdoc cref="EnumValue{TEnum}.Parse(string)"/>
267+
public static TEnum Parse<TEnum>(string value, bool ignoreCase)
268+
where TEnum : Enum
269+
=> ignoreCase
270+
? EnumValueCaseIgnored<TEnum>.Parse(value)
271+
: EnumValue<TEnum>.Parse(value);
272+
273+
/// <inheritdoc cref="EnumValue{TEnum}.TryParse(string, out TEnum)"/>
274+
public static bool TryParse<TEnum>(string value, out TEnum e)
275+
where TEnum : Enum
276+
=> EnumValue<TEnum>.TryParse(value, out e);
277+
278+
/// <inheritdoc cref="EnumValue{TEnum}.TryParse(string, out TEnum)"/>
279+
/// <summary>
280+
/// Converts the string representation of the name of one or more enumerated constants to an equivalent enumerated object.
281+
/// A parameter specifies whether the operation is case-insensitive.
282+
/// </summary>
283+
/// <param name="ignoreCase">If true, will ignore case differences when looking for a match.</param>
284+
public static bool TryParse<TEnum>(string value, bool ignoreCase, out TEnum e)
285+
where TEnum : Enum
286+
=> ignoreCase
287+
? EnumValueCaseIgnored<TEnum>.TryParse(value, out e)
288+
: EnumValue<TEnum>.TryParse(value, out e);
289+
}
261290
}

Source/Open.Text.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Text</RepositoryUrl>
1818
<RepositoryType>git</RepositoryType>
1919
<PackageTags>dotnet, dotnetcore, string, span, readonlyspan, text, format, split, trim, equals, trimmed equals, first, last, preceding, following, stringbuilder, extensions</PackageTags>
20-
<Version>3.3.0</Version>
20+
<Version>3.3.1</Version>
2121
<PackageReleaseNotes></PackageReleaseNotes>
2222
<PackageLicenseExpression>MIT</PackageLicenseExpression>
2323
<PublishRepositoryUrl>true</PublishRepositoryUrl>

0 commit comments

Comments
 (0)