Skip to content

Commit a5df4c4

Browse files
committed
.NET Standard 2.0 support, tests and cleanup
1 parent f9f3e5f commit a5df4c4

15 files changed

+292
-177
lines changed

Directory.Build.props

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project>
22
<PropertyGroup>
33
<LangVersion>12.0</LangVersion>
4-
<Nullable>enable</Nullable>
4+
<Nullable Condition="'$(TargetFramework)' != 'netstandard2.0'">enable</Nullable>
5+
<Nullable Condition="'$(TargetFramework)' == 'netstandard2.0'">annotations</Nullable>
56
<ImplicitUsings>enable</ImplicitUsings>
67
<GenerateDocumentationFile>true</GenerateDocumentationFile>
78

Singulink.Globalization.Currency.sln

+7
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{764A
4141
.github\dependabot.yml = .github\dependabot.yml
4242
EndProjectSection
4343
EndProject
44+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Singulink.Globalization.Currency.NetStandardTests", "Tests\Singulink.Globalization.Currency.NetStandardTests\Singulink.Globalization.Currency.NetStandardTests.csproj", "{A95689C0-68A0-4457-9B2C-0357468920AE}"
45+
EndProject
4446
Global
4547
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4648
Debug|Any CPU = Debug|Any CPU
@@ -55,6 +57,10 @@ Global
5557
{FE384100-51BA-42C4-B269-F0C3BCF5A6BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
5658
{FE384100-51BA-42C4-B269-F0C3BCF5A6BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
5759
{FE384100-51BA-42C4-B269-F0C3BCF5A6BE}.Release|Any CPU.Build.0 = Release|Any CPU
60+
{A95689C0-68A0-4457-9B2C-0357468920AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61+
{A95689C0-68A0-4457-9B2C-0357468920AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
62+
{A95689C0-68A0-4457-9B2C-0357468920AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
63+
{A95689C0-68A0-4457-9B2C-0357468920AE}.Release|Any CPU.Build.0 = Release|Any CPU
5864
EndGlobalSection
5965
GlobalSection(SolutionProperties) = preSolution
6066
HideSolutionNode = FALSE
@@ -64,6 +70,7 @@ Global
6470
{FE384100-51BA-42C4-B269-F0C3BCF5A6BE} = {DBA8BA65-D53C-41A8-9534-EAB6AE159CCB}
6571
{97D96E55-E2D9-4076-8D71-BA319A703C06} = {DBA8BA65-D53C-41A8-9534-EAB6AE159CCB}
6672
{4028E49D-6194-47FA-AEA9-5B046F8013D2} = {2CD9744C-7FC1-485B-BB89-406676D56D25}
73+
{A95689C0-68A0-4457-9B2C-0357468920AE} = {2CD9744C-7FC1-485B-BB89-406676D56D25}
6774
EndGlobalSection
6875
GlobalSection(ExtensibilityGlobals) = postSolution
6976
SolutionGuid = {06D2E21A-73BC-4E56-B817-7147C8D79C05}

Source/Singulink.Globalization.Currency/Currency.cs

+25-12
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class Currency : IFormattable
3535
public Money MinorUnit => new Money(new decimal(1, 0, 0, false, (byte)DecimalDigits), this);
3636

3737
/// <summary>
38-
/// Gets a list containing language identifers and currency names.
38+
/// Gets a list containing language identifiers and currency names.
3939
/// </summary>
4040
public IEnumerable<(string CultureName, string Name)> LocalizedNames => _localizedNameLookup?.Select(kvp => (kvp.Key, kvp.Value)) ?? Array.Empty<(string, string)>();
4141

@@ -54,7 +54,7 @@ public class Currency : IFormattable
5454
/// is not found.
5555
/// </remarks>
5656
public Currency(string name, string currencyCode, string symbol, int decimalDigits, params (string CultureName, string Name)[] localizedNames)
57-
: this(name, currencyCode, symbol, decimalDigits, localizedNames.Length == 0 ? null : localizedNames.AsEnumerable()) { }
57+
: this(name, currencyCode, symbol, decimalDigits, localizedNames.Length is 0 ? null : localizedNames.AsEnumerable()) { }
5858

5959
/// <inheritdoc cref="Currency(string, string, string, int, ValueTuple{string, string}[])"/>
6060
public Currency(string name, string currencyCode, string symbol, int decimalDigits, IEnumerable<(string CultureName, string Name)>? localizedNames = null)
@@ -63,16 +63,16 @@ public Currency(string name, string currencyCode, string symbol, int decimalDigi
6363
name = name.Trim();
6464
symbol = symbol.Trim();
6565

66-
if (currencyCode.Length == 0)
66+
if (currencyCode.Length is 0)
6767
throw new ArgumentException("Currency code is required.", nameof(currencyCode));
6868

6969
if (currencyCode.Length > 20)
7070
throw new ArgumentOutOfRangeException(nameof(currencyCode), "Currency code has a maximum length of 20 characters.");
7171

72-
if (name.Length == 0)
72+
if (name.Length is 0)
7373
throw new ArgumentException("Name is required.", nameof(name));
7474

75-
if (symbol.Length == 0)
75+
if (symbol.Length is 0)
7676
throw new ArgumentException("Symbol is required.", nameof(symbol));
7777

7878
if (symbol.Length > 20)
@@ -86,14 +86,23 @@ public Currency(string name, string currencyCode, string symbol, int decimalDigi
8686
Symbol = symbol;
8787
DecimalDigits = decimalDigits;
8888

89-
if (localizedNames != null)
89+
if (localizedNames is not null)
9090
{
91-
foreach (var (cultureName, localName) in localizedNames)
91+
foreach (var localizedName in localizedNames)
9292
{
93+
string cultureName = CoerceCultureName(localizedName.CultureName);
94+
string localName = CoerceCurrencyName(localizedName.Name);
95+
9396
_localizedNameLookup ??= new(StringComparer.OrdinalIgnoreCase);
97+
#if NETSTANDARD
98+
if (_localizedNameLookup.ContainsKey(cultureName))
99+
throw new ArgumentException($"Duplicate culture name '{cultureName}' in localized names.", nameof(localizedNames));
94100

95-
if (!_localizedNameLookup.TryAdd(CoerceCultureName(cultureName), CoerceCurrencyName(localName)))
101+
_localizedNameLookup.Add(cultureName, localName);
102+
#else
103+
if (!_localizedNameLookup.TryAdd(cultureName, localName))
96104
throw new ArgumentException($"Duplicate culture name '{cultureName}' in localized names.", nameof(localizedNames));
105+
#endif
97106
}
98107
}
99108

@@ -159,15 +168,15 @@ public string ToString(string? format, CultureInfo? culture = null)
159168

160169
string name;
161170

162-
if (_localizedNameLookup == null)
171+
if (_localizedNameLookup is null)
163172
{
164173
name = Name;
165174
}
166175
else if (!_localizedNameLookup.TryGetValue(culture.Name, out name))
167176
{
168177
var neutralCulture = culture.GetNeutralCulture();
169178

170-
if (neutralCulture == null || !_localizedNameLookup.TryGetValue(neutralCulture.Name, out name))
179+
if (neutralCulture is null || !_localizedNameLookup.TryGetValue(neutralCulture.Name, out name))
171180
name = Name;
172181
}
173182

@@ -220,9 +229,13 @@ internal static CurrencyRegistry CreateSystemRegistry()
220229

221230
if (localizedNameLookup.TryGetValue(localizationCultureName, out string existingLocalizedName) && existingLocalizedName != localizedName)
222231
{
223-
// This shouldn't happen, but if the data changes and it does then make this future-proof by adding the localized name to the specific
224-
// culture instead to override the different neutral culture name that was set.
232+
// This shouldn't happen in .NET+, but if the data changes and it does then make this future-proof by adding the localized name to the
233+
// specific culture instead to override the different neutral culture name that was set.
234+
235+
// This *does* happen on .NET Framework, so neutral culture name will be whatever specific culture name was first.
236+
#if !NETSTANDARD
225237
Debug.Fail("Neutral localization culture name was already set to a different name.");
238+
#endif
226239
localizedNameLookup.Add(culture.Name, localizedName);
227240
}
228241
else

Source/Singulink.Globalization.Currency/CurrencyRegistry.cs

+19-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ namespace Singulink.Globalization;
77
/// <summary>
88
/// Represents a collection of currencies.
99
/// </summary>
10-
public sealed class CurrencyRegistry : ISet<Currency>, IReadOnlySet<Currency>
10+
public sealed class CurrencyRegistry : ISet<Currency>
11+
#if !NETSTANDARD
12+
#pragma warning disable SA1001 // Commas should be spaced correctly
13+
, IReadOnlySet<Currency>
14+
#pragma warning restore SA1001
15+
#endif
1116
{
1217
private static CurrencyRegistry? _default;
1318
private static CurrencyRegistry? _system;
@@ -25,16 +30,27 @@ public CurrencyRegistry(string name, IEnumerable<Currency> currencies)
2530
{
2631
_name = name.Trim();
2732

28-
if (_name.Length == 0)
33+
if (_name.Length is 0)
2934
throw new ArgumentException("Name is required.", nameof(name));
3035

3136
_currencies = [];
3237
_currencyLookup = new(StringComparer.OrdinalIgnoreCase);
3338

3439
foreach (var currency in currencies)
3540
{
41+
#if NETSTANDARD
42+
if (_currencies.Add(currency))
43+
{
44+
if (_currencyLookup.ContainsKey(currency.CurrencyCode))
45+
throw new ArgumentException($"Multiple currencies with currency code '{currency.CurrencyCode}'.", nameof(currencies));
46+
47+
_currencyLookup.Add(currency.CurrencyCode, currency);
48+
}
49+
#else
3650
if (_currencies.Add(currency) && !_currencyLookup.TryAdd(currency.CurrencyCode, currency))
3751
throw new ArgumentException($"Multiple currencies with currency code '{currency.CurrencyCode}'.", nameof(currencies));
52+
#endif
53+
3854
}
3955
}
4056

@@ -51,7 +67,7 @@ public static CurrencyRegistry Default
5167
{
5268
get => _default ??= System;
5369
set {
54-
if (_default != null)
70+
if (_default is not null)
5571
throw new InvalidOperationException("Default currency registry cannot be set after it has already been set or accessed.");
5672

5773
_default = value;

Source/Singulink.Globalization.Currency/IImmutableMoneySet.cs

-27
Original file line numberDiff line numberDiff line change
@@ -153,31 +153,4 @@ public interface IImmutableMoneySet : ICollection<Money>, IReadOnlyMoneySet
153153
/// Removes all zero amounts from this set and returns the resulting set.
154154
/// </summary>
155155
public IImmutableMoneySet TrimZeroAmounts();
156-
157-
#region Explicit Interface Implementations
158-
159-
/// <summary>
160-
/// Gets a value indicating whether the set is read-only. Always returns <see langword="true"/>.
161-
/// </summary>
162-
bool ICollection<Money>.IsReadOnly => true;
163-
164-
/// <summary>
165-
/// Not supported.
166-
/// </summary>
167-
/// <exception cref="NotSupportedException">This operation is not supported.</exception>
168-
void ICollection<Money>.Add(Money item) => throw new NotSupportedException();
169-
170-
/// <summary>
171-
/// Not supported.
172-
/// </summary>
173-
/// <exception cref="NotSupportedException">This operation is not supported.</exception>
174-
void ICollection<Money>.Clear() => throw new NotSupportedException();
175-
176-
/// <summary>
177-
/// Not supported.
178-
/// </summary>
179-
/// <exception cref="NotSupportedException">This operation is not supported.</exception>
180-
bool ICollection<Money>.Remove(Money item) => throw new NotSupportedException();
181-
182-
#endregion
183156
}

0 commit comments

Comments
 (0)