From adf9521f21cbc3bc7e6e0daa75d840ec3ce02157 Mon Sep 17 00:00:00 2001 From: Andreas Gullberg Larsen Date: Thu, 26 Dec 2024 19:26:36 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=A5Require=20unit=20enum=20types=20to?= =?UTF-8?q?=20also=20be=20struct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change all `where TUnitType : Enum` generic type constraints to include `struct` constraint, to avoid usages like this. ```cs UnitsNetSetup.Default.UnitParser.Parse("abc"); // Ok UnitsNetSetup.Default.UnitParser.Parse("abc"); // Compiles, but throws exception ``` This also fixes inconsistency, between `UnitAbbreviationsCache.GetUnitAbbreviations` and `UnitAbbreviationsCache.GetDefaultAbbreviation`. ### Changes - Add `struct` constraint to all `Enum` constraints - Remove two test cases `MapAndLookup_MapWithSpecificEnumType_LookupWithEnumType`, `MapAndLookup_WithEnumType` ### Noteworthy There are some minor use cases for passing non-generic `Enum` value, like getting unit abbreviations given a `IQuantity` such as `UnitAbbreviations.GetDefaultAbbreviation(quantity.Unit)`. Now this requires using `GetDefaultAbbreviation(quantity.Unit.GetType(), Convert.ToInt32(quantity.Unit))` instead. However, `GetAbbreviations()` already had this requirement, so now it's more consistent. ### Sources https://stackoverflow.com/a/74773273 https://devblogs.microsoft.com/premier-developer/dissecting-new-generics-constraints-in-c-7-3/ --- UnitsNet.Tests/UnitAbbreviationsCacheTests.cs | 17 ----------------- UnitsNet/CustomCode/QuantityParser.cs | 12 ++++++------ UnitsNet/CustomCode/UnitAbbreviationsCache.cs | 12 ++++++------ UnitsNet/CustomCode/UnitParser.cs | 4 ++-- UnitsNet/IArithmeticQuantity.cs | 2 +- UnitsNet/IQuantity.cs | 4 ++-- UnitsNet/QuantityDisplay.cs | 6 +++--- UnitsNet/QuantityFormatter.cs | 18 +++++++++--------- UnitsNet/QuantityInfo.cs | 2 +- UnitsNet/UnitFormatter.cs | 2 +- UnitsNet/UnitInfo.cs | 2 +- UnitsNet/UnitMath.cs | 16 ++++++++-------- 12 files changed, 40 insertions(+), 57 deletions(-) diff --git a/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs b/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs index 7f360f8c08..0b1aba6145 100644 --- a/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs +++ b/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs @@ -404,23 +404,6 @@ public void MapAndLookup_WithSpecificEnumType() Assert.Equal("sm", UnitAbbreviationsCache.Default.GetDefaultAbbreviation(HowMuchUnit.Some)); } - /// - [Fact] - public void MapAndLookup_WithEnumType() - { - Enum valueAsEnumType = HowMuchUnit.Some; - UnitAbbreviationsCache.Default.MapUnitToDefaultAbbreviation(valueAsEnumType, "sm"); - Assert.Equal("sm", UnitAbbreviationsCache.Default.GetDefaultAbbreviation(valueAsEnumType)); - } - - /// - [Fact] - public void MapAndLookup_MapWithSpecificEnumType_LookupWithEnumType() - { - UnitAbbreviationsCache.Default.MapUnitToDefaultAbbreviation(HowMuchUnit.Some, "sm"); - Assert.Equal("sm", UnitAbbreviationsCache.Default.GetDefaultAbbreviation((Enum)HowMuchUnit.Some)); - } - /// /// Convenience method to the proper culture parameter type. /// diff --git a/UnitsNet/CustomCode/QuantityParser.cs b/UnitsNet/CustomCode/QuantityParser.cs index e6d3ef1a35..b6f8b609a2 100644 --- a/UnitsNet/CustomCode/QuantityParser.cs +++ b/UnitsNet/CustomCode/QuantityParser.cs @@ -19,7 +19,7 @@ namespace UnitsNet /// The type of unit enum that belongs to this quantity, such as for . public delegate TQuantity QuantityFromDelegate(double value, TUnitType fromUnit) where TQuantity : IQuantity - where TUnitType : Enum; + where TUnitType : struct, Enum; /// /// Parses quantities from strings, such as "1.2 kg" to or "100 cm" to . @@ -67,7 +67,7 @@ public TQuantity Parse(string str, IFormatProvider? formatProvider, QuantityFromDelegate fromDelegate) where TQuantity : IQuantity - where TUnitType : Enum + where TUnitType : struct, Enum { if (str == null) throw new ArgumentNullException(nameof(str)); str = str.Trim(); @@ -154,7 +154,7 @@ internal string CreateRegexPatternForUnit( TUnitType unit, IFormatProvider? formatProvider, bool matchEntireString = true) - where TUnitType : Enum + where TUnitType : struct, Enum { var unitAbbreviations = _unitAbbreviationsCache.GetUnitAbbreviations(unit, formatProvider); var pattern = GetRegexPatternForUnitAbbreviations(unitAbbreviations); @@ -181,7 +181,7 @@ private TQuantity ParseWithRegex(string valueString, QuantityFromDelegate fromDelegate, IFormatProvider? formatProvider) where TQuantity : IQuantity - where TUnitType : Enum + where TUnitType : struct, Enum { var value = double.Parse(valueString, ParseNumberStyles, formatProvider); var parsedUnit = _unitParser.Parse(unitString, formatProvider); @@ -242,7 +242,7 @@ private static bool TryExtractValueAndUnit(Regex regex, string str, [NotNullWhen return true; } - private string CreateRegexPatternForQuantity(IFormatProvider? formatProvider) where TUnitType : Enum + private string CreateRegexPatternForQuantity(IFormatProvider? formatProvider) where TUnitType : struct, Enum { var unitAbbreviations = _unitAbbreviationsCache.GetAllUnitAbbreviationsForQuantity(typeof(TUnitType), formatProvider); var pattern = GetRegexPatternForUnitAbbreviations(unitAbbreviations); @@ -251,7 +251,7 @@ private string CreateRegexPatternForQuantity(IFormatProvider? formatP return $"^{pattern}$"; } - private Regex CreateRegexForQuantity(IFormatProvider? formatProvider) where TUnitType : Enum + private Regex CreateRegexForQuantity(IFormatProvider? formatProvider) where TUnitType : struct, Enum { var pattern = CreateRegexPatternForQuantity(formatProvider); return new Regex(pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase); diff --git a/UnitsNet/CustomCode/UnitAbbreviationsCache.cs b/UnitsNet/CustomCode/UnitAbbreviationsCache.cs index 273629a7db..b64aa16711 100644 --- a/UnitsNet/CustomCode/UnitAbbreviationsCache.cs +++ b/UnitsNet/CustomCode/UnitAbbreviationsCache.cs @@ -88,7 +88,7 @@ internal UnitAbbreviationsCache(QuantityInfoLookup quantityInfoLookup) /// The unit enum value. /// Unit abbreviations to add. /// The type of unit enum. - public void MapUnitToAbbreviation(TUnitType unit, params string[] abbreviations) where TUnitType : Enum + public void MapUnitToAbbreviation(TUnitType unit, params string[] abbreviations) where TUnitType : struct, Enum { PerformAbbreviationMapping(unit, CultureInfo.CurrentCulture, false, abbreviations); } @@ -101,7 +101,7 @@ public void MapUnitToAbbreviation(TUnitType unit, params string[] abb /// The unit enum value. /// Unit abbreviations to add as default. /// The type of unit enum. - public void MapUnitToDefaultAbbreviation(TUnitType unit, string abbreviation) where TUnitType : Enum + public void MapUnitToDefaultAbbreviation(TUnitType unit, string abbreviation) where TUnitType : struct, Enum { PerformAbbreviationMapping(unit, CultureInfo.CurrentCulture, true, abbreviation); } @@ -115,7 +115,7 @@ public void MapUnitToDefaultAbbreviation(TUnitType unit, string abbre /// The format provider to use for lookup. Defaults to if null. /// Unit abbreviations to add. /// The type of unit enum. - public void MapUnitToAbbreviation(TUnitType unit, IFormatProvider? formatProvider, params string[] abbreviations) where TUnitType : Enum + public void MapUnitToAbbreviation(TUnitType unit, IFormatProvider? formatProvider, params string[] abbreviations) where TUnitType : struct, Enum { PerformAbbreviationMapping(unit, formatProvider, false, abbreviations); } @@ -129,7 +129,7 @@ public void MapUnitToAbbreviation(TUnitType unit, IFormatProvider? fo /// The format provider to use for lookup. Defaults to if null. /// Unit abbreviation to add as default. /// The type of unit enum. - public void MapUnitToDefaultAbbreviation(TUnitType unit, IFormatProvider? formatProvider, string abbreviation) where TUnitType : Enum + public void MapUnitToDefaultAbbreviation(TUnitType unit, IFormatProvider? formatProvider, string abbreviation) where TUnitType : struct, Enum { PerformAbbreviationMapping(unit, formatProvider, true, abbreviation); } @@ -183,7 +183,7 @@ private void PerformAbbreviationMapping(Enum unitValue, IFormatProvider? formatP /// The format provider to use for lookup. Defaults to if null. /// The type of unit enum. /// The default unit abbreviation string. - public string GetDefaultAbbreviation(TUnitType unit, IFormatProvider? formatProvider = null) where TUnitType : Enum + public string GetDefaultAbbreviation(TUnitType unit, IFormatProvider? formatProvider = null) where TUnitType : struct, Enum { Type unitType = typeof(TUnitType); @@ -215,7 +215,7 @@ public string GetDefaultAbbreviation(Type unitType, int unitValue, IFormatProvid /// Enum value for unit. /// The format provider to use for lookup. Defaults to if null. /// Unit abbreviations associated with unit. - public string[] GetUnitAbbreviations(TUnitType unit, IFormatProvider? formatProvider = null) where TUnitType : Enum + public string[] GetUnitAbbreviations(TUnitType unit, IFormatProvider? formatProvider = null) where TUnitType : struct, Enum { return GetUnitAbbreviations(typeof(TUnitType), Convert.ToInt32(unit), formatProvider); } diff --git a/UnitsNet/CustomCode/UnitParser.cs b/UnitsNet/CustomCode/UnitParser.cs index 740c44284f..f954e0da5e 100644 --- a/UnitsNet/CustomCode/UnitParser.cs +++ b/UnitsNet/CustomCode/UnitParser.cs @@ -46,7 +46,7 @@ public UnitParser(UnitAbbreviationsCache? unitAbbreviationsCache) /// /// public TUnitType Parse(string unitAbbreviation, IFormatProvider? formatProvider = null) - where TUnitType : Enum + where TUnitType : struct, Enum { return (TUnitType)Parse(unitAbbreviation, typeof(TUnitType), formatProvider); } @@ -205,7 +205,7 @@ public bool TryParse([NotNullWhen(true)] string? unitAbbreviation, Type unitType { return false; } - + unit = matches[0].Unit; return true; } diff --git a/UnitsNet/IArithmeticQuantity.cs b/UnitsNet/IArithmeticQuantity.cs index 031a4510e0..7bb86eaa4e 100644 --- a/UnitsNet/IArithmeticQuantity.cs +++ b/UnitsNet/IArithmeticQuantity.cs @@ -21,7 +21,7 @@ public interface IArithmeticQuantity : IQuantity #endif where TSelf : IArithmeticQuantity - where TUnitType : Enum + where TUnitType : struct, Enum { #if NET7_0_OR_GREATER /// diff --git a/UnitsNet/IQuantity.cs b/UnitsNet/IQuantity.cs index 2fec35869f..d9e6c2c221 100644 --- a/UnitsNet/IQuantity.cs +++ b/UnitsNet/IQuantity.cs @@ -111,7 +111,7 @@ public interface IQuantity : IFormattable /// /// The unit type of the quantity. public interface IQuantity : IQuantity - where TUnitType : Enum + where TUnitType : struct, Enum { /// /// Convert to a unit representation . @@ -149,7 +149,7 @@ public interface IQuantity : IQuantity /// The underlying unit enum type. public interface IQuantity : IQuantity where TSelf : IQuantity - where TUnitType : Enum + where TUnitType : struct, Enum { /// /// diff --git a/UnitsNet/QuantityDisplay.cs b/UnitsNet/QuantityDisplay.cs index 90a9513359..71c0d25ec5 100644 --- a/UnitsNet/QuantityDisplay.cs +++ b/UnitsNet/QuantityDisplay.cs @@ -37,7 +37,7 @@ public AbbreviationDisplay(IQuantity quantity) } [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public string DefaultAbbreviation => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(_quantity.Unit); + public string DefaultAbbreviation => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(_quantity.Unit.GetType(), Convert.ToInt32(_quantity.Unit)); [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public string[] Abbreviations => @@ -55,7 +55,7 @@ internal readonly struct ConvertedQuantity(IQuantity baseQuantity, Enum unit) public IQuantity Quantity => baseQuantity.ToUnit(Unit); [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public string Abbreviation => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(Unit); + public string Abbreviation => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(Unit.GetType(), Convert.ToInt32(Unit)); public override string ToString() { @@ -129,7 +129,7 @@ internal readonly struct StringFormatsDisplay(IQuantity quantity) internal readonly struct ConvertedQuantity(IQuantity quantity) { public Enum Unit => Quantity.Unit; - public string Abbreviation => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(Quantity.Unit); + public string Abbreviation => UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(Quantity.Unit.GetType(), Convert.ToInt32(Quantity.Unit)); public ValueDisplay Value => new(Quantity); public IQuantity Quantity { get; } = quantity; diff --git a/UnitsNet/QuantityFormatter.cs b/UnitsNet/QuantityFormatter.cs index ad9b3e647e..34148b3866 100644 --- a/UnitsNet/QuantityFormatter.cs +++ b/UnitsNet/QuantityFormatter.cs @@ -25,7 +25,7 @@ public class QuantityFormatter /// Any of the /// /// Standard format specifiers - /// . + /// . /// /// /// @@ -63,11 +63,11 @@ public class QuantityFormatter /// The string representation. /// Thrown when the format specifier is invalid. public static string Format(IQuantity quantity, string format) - where TUnitType : Enum + where TUnitType : struct, Enum { return Format(quantity, format, CultureInfo.CurrentCulture); } - + /// /// Formats a quantity using the given format string and format provider. /// @@ -86,7 +86,7 @@ public static string Format(IQuantity quantity, string for /// Any of the /// /// Standard format specifiers - /// . + /// . /// /// /// @@ -124,13 +124,13 @@ public static string Format(IQuantity quantity, string for /// The string representation. /// Thrown when the format specifier is invalid. public static string Format(IQuantity quantity, string? format, IFormatProvider? formatProvider) - where TUnitType : Enum + where TUnitType : struct, Enum { return FormatUntrimmed(quantity, format, formatProvider).TrimEnd(); } private static string FormatUntrimmed(IQuantity quantity, string? format, IFormatProvider? formatProvider) - where TUnitType : Enum + where TUnitType : struct, Enum { formatProvider ??= CultureInfo.CurrentCulture; if (format is null) @@ -202,20 +202,20 @@ private static string FormatUntrimmed(IQuantity quantity, #endif } } - + // Anything else is a standard numeric format string with default unit abbreviation postfix. return FormatWithValueAndAbbreviation(quantity, format, formatProvider); } private static string FormatWithValueAndAbbreviation(IQuantity quantity, string format, IFormatProvider formatProvider) - where TUnitType : Enum + where TUnitType : struct, Enum { var abbreviation = UnitsNetSetup.Default.UnitAbbreviations.GetDefaultAbbreviation(quantity.Unit, formatProvider); return string.Format(formatProvider, $"{{0:{format}}} {{1}}", quantity.Value, abbreviation); } private static string ToStringWithSignificantDigitsAfterRadix(IQuantity quantity, IFormatProvider formatProvider, int number) - where TUnitType : Enum + where TUnitType : struct, Enum { var formatForSignificantDigits = UnitFormatter.GetFormat(quantity.Value, number); var formatArgs = UnitFormatter.GetFormatArgs(quantity.Unit, quantity.Value, formatProvider, []); diff --git a/UnitsNet/QuantityInfo.cs b/UnitsNet/QuantityInfo.cs index 96cf53ffa1..6e9caa78fa 100644 --- a/UnitsNet/QuantityInfo.cs +++ b/UnitsNet/QuantityInfo.cs @@ -130,7 +130,7 @@ public IEnumerable GetUnitInfosFor(BaseUnits baseUnits) /// /// The unit enum type, such as . public class QuantityInfo : QuantityInfo - where TUnit : Enum + where TUnit : struct, Enum { /// public QuantityInfo(string name, UnitInfo[] unitInfos, TUnit baseUnit, IQuantity zero, BaseDimensions baseDimensions) diff --git a/UnitsNet/UnitFormatter.cs b/UnitsNet/UnitFormatter.cs index 06ecbbf700..87cb382ba7 100644 --- a/UnitsNet/UnitFormatter.cs +++ b/UnitsNet/UnitFormatter.cs @@ -71,7 +71,7 @@ private static bool NearlyEqual(double a, double b) /// The list of format arguments. /// An array of ToString format arguments. public static object[] GetFormatArgs(TUnitType unit, double value, IFormatProvider? culture, IEnumerable args) - where TUnitType : Enum + where TUnitType : struct, Enum { string abbreviation = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(typeof(TUnitType), Convert.ToInt32(unit), culture); return new object[] {value, abbreviation}.Concat(args).ToArray(); diff --git a/UnitsNet/UnitInfo.cs b/UnitsNet/UnitInfo.cs index 9f42a1e23e..576b5c6d4d 100644 --- a/UnitsNet/UnitInfo.cs +++ b/UnitsNet/UnitInfo.cs @@ -82,7 +82,7 @@ public UnitInfo(Enum value, string pluralName, BaseUnits baseUnits, string quant /// /// The unit enum type, such as . public class UnitInfo : UnitInfo - where TUnit : Enum + where TUnit : struct, Enum { /// [Obsolete("Use the constructor that also takes a quantityName parameter.")] diff --git a/UnitsNet/UnitMath.cs b/UnitsNet/UnitMath.cs index 22c8ee1915..e68099184f 100644 --- a/UnitsNet/UnitMath.cs +++ b/UnitsNet/UnitMath.cs @@ -31,7 +31,7 @@ public static TQuantity Abs(this TQuantity value) where TQuantity : I /// source contains quantity types different from . /// public static TQuantity Sum(this IEnumerable source, TUnitType unitType) - where TUnitType : Enum + where TUnitType : struct, Enum where TQuantity : IQuantity { return (TQuantity) Quantity.From(source.Sum(x => x.As(unitType)), unitType); @@ -55,7 +55,7 @@ public static TQuantity Sum(this IEnumerable so /// source contains quantity types different from . /// public static TQuantity Sum(this IEnumerable source, Func selector, TUnitType unitType) - where TUnitType : Enum + where TUnitType : struct, Enum where TQuantity : IQuantity { return source.Select(selector).Sum(unitType); @@ -83,7 +83,7 @@ public static TQuantity Min(TQuantity val1, TQuantity val2) where TQu /// source contains quantity types different from . /// public static TQuantity Min(this IEnumerable source, TUnitType unitType) - where TUnitType : Enum + where TUnitType : struct, Enum where TQuantity : IQuantity { return (TQuantity) Quantity.From(source.Min(x => x.As(unitType)), unitType); @@ -108,7 +108,7 @@ public static TQuantity Min(this IEnumerable so /// source contains quantity types different from . /// public static TQuantity Min(this IEnumerable source, Func selector, TUnitType unitType) - where TUnitType : Enum + where TUnitType : struct, Enum where TQuantity : IQuantity { return source.Select(selector).Min(unitType); @@ -136,7 +136,7 @@ public static TQuantity Max(TQuantity val1, TQuantity val2) where TQu /// source contains quantity types different from . /// public static TQuantity Max(this IEnumerable source, TUnitType unitType) - where TUnitType : Enum + where TUnitType : struct, Enum where TQuantity : IQuantity { return (TQuantity) Quantity.From(source.Max(x => x.As(unitType)), unitType); @@ -161,7 +161,7 @@ public static TQuantity Max(this IEnumerable so /// source contains quantity types different from . /// public static TQuantity Max(this IEnumerable source, Func selector, TUnitType unitType) - where TUnitType : Enum + where TUnitType : struct, Enum where TQuantity : IQuantity { return source.Select(selector).Max(unitType); @@ -179,7 +179,7 @@ public static TQuantity Max(this IEnumerablesource contains quantity types different from . /// public static TQuantity Average(this IEnumerable source, TUnitType unitType) - where TUnitType : Enum + where TUnitType : struct, Enum where TQuantity : IQuantity { return (TQuantity) Quantity.From(source.Average(x => x.As(unitType)), unitType); @@ -204,7 +204,7 @@ public static TQuantity Average(this IEnumerablesource contains quantity types different from . /// public static TQuantity Average(this IEnumerable source, Func selector, TUnitType unitType) - where TUnitType : Enum + where TUnitType : struct, Enum where TQuantity : IQuantity { return source.Select(selector).Average(unitType);