Replaced the IFormatProvider parameter with CultureInfo for all unit-related operations - #1542
Replaced the IFormatProvider parameter with CultureInfo for all unit-related operations#1542lipchev wants to merge 1 commit into
IFormatProvider parameter with CultureInfo for all unit-related operations#1542Conversation
…nit-related operations
|
@angularsen If you agree with the premise, this should be easy to review - I intentionally did not touch anything but the parameter types and the xml-docs. This is certainly a breaking change, but my guess is that it won't be that bad- this should only affect users that are passing a parameter of type If this is all acceptable, then I'll just do a quick-reformat, before adding the rest of the modifications to the |
angularsen
left a comment
There was a problem hiding this comment.
I'm having second thoughts, see comments
| @@ -186,7 +186,7 @@ private TQuantity ParseWithRegex<TQuantity, TUnitType>(string valueString, | |||
| where TUnitType : struct, Enum | |||
There was a problem hiding this comment.
Why isn't this IFormatProvider also changed to CultureInfo?
double.Parse can take a CultureInfo, so why should we not just standardize on CultureInfo here and other places?
I tried reading up a bit on IFormatProvider, and its remarks section: https://learn.microsoft.com/en-us/dotnet/api/system.iformatprovider?view=net-9.0#remarks
The whole purpose of .NET standardizing on IFormatProvider, seems to me, is to support different custom formatters for string.Format(), ToString() etc.
There are 3 built in types: NumberFormatInfo, DateTimeFormatInfo and CultureInfo.
CultureInfo holds both number formatting and date formatting, which makes this whole thing a bit weird:
var culture = CultureInfo.GetCultureInfo("en-US");
// Equivalent, so double.Parse() can take both CultureInfo and NumberFormatInfo
double.Parse("5.5", culture);
double.Parse("5.5", culture.NumberFormat);
// Only works if CurrentCulture is compatible, so it basically ignores incompatible `IFormatProvider` implementations
double.Parse("5.5", culture.DateTimeFormat); This is because double.Parse() calls NumberFormatInfo.GetInstance(provider) to select NumberFormatInfo when given either CultureInfo or NumberFormatInfo, otherwise it falls back to CurrentCulture.
public static double Parse(String s, NumberStyles style, IFormatProvider provider) {
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
return Parse(s, style, NumberFormatInfo.GetInstance(provider));
}https://github.com/microsoft/referencesource/blob/master/mscorlib/system/double.cs#L235-L251
Long story short, I have a feeling it's maybe not a good idea for us to ditch IFormatProvider in our public methods, even if we do require CultureInfo in the end. Similar to how .NET's own types standardize on IFormatProvider and has internal logic to obtain the compatible formatter, if any, or fallback to CurrentCulture.
To follow .NET conventions, I think we should just should do the same and cast internally, ignoring anything except CultureInfo for localization, and falling back to CurrentCulture.
There was a problem hiding this comment.
The reason why IFormatProvider is used in ToString and Parse is because they don't have any assumptions about whether the CultureInfo or NumberFormat (or the DateInfoFormat) is required by the target type.
On the other hand, CultureInfo is used for localizing strings (not sure if this is changing with the StringLocalizer) where only the culture name is the key.
In our case Mass.Zero.ToString should take an IFormatProvider, passing it to the Value (which uses the associated NumberFormat) and but type-casting it to a CultureInfo when accessing the ResourceDictionary.
Anyways, if you don't want to change it that's ok, I use nothing but InvariantCulture when dealing with the units.
There was a problem hiding this comment.
That's my point, double.Parse indeed has an assumption on the formatter it wants, but it still supports IFormatProvider in order to accept both NumberFormatInfo and CultureInfo. I guess several reasons, but consistency and also to make it easier for the developer to just pass whatever they have, instead of having to do myCulture.NumberFormat every time.
A similar point goes for us, since c# code typically passes IFormatProvider around, they'll have to start casting to CultureInfo just to satisfy our public signature. I don't see much benefit to require a concrete type, but I see how it can cause annoying friction.
Let's stay with IFormatProvider.
|
@angularsen I've reverted the parameters in 53f00cd
I've got an extended weekend so I'll try to do this and perhaps a PR for removing the |
As mentioned in #1509 , since all unit-related operations ultimately end up with a call to the
ResourceManagerexpecting aCultureInfo, there is no point in using anIFormatProviderfor the method parameter.This does not affect the
Quantity.Parsemethods, which also depend on theNumberFormat(theIFormatProviderparameter is safe-cast when calling theUnitParser).