< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 355
Coverable lines: 355
Total lines: 878
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 188
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Runtime.CompilerServices;
 6using System.Text;
 7
 8namespace System.Globalization
 9{
 10    /// <remarks>
 11    /// Property             Default Description
 12    /// PositiveSign           '+'   Character used to indicate positive values.
 13    /// NegativeSign           '-'   Character used to indicate negative values.
 14    /// NumberDecimalSeparator '.'   The character used as the decimal separator.
 15    /// NumberGroupSeparator   ','   The character used to separate groups of
 16    ///                              digits to the left of the decimal point.
 17    /// NumberDecimalDigits    2     The default number of decimal places.
 18    /// NumberGroupSizes       3     The number of digits in each group to the
 19    ///                              left of the decimal point.
 20    /// NaNSymbol             "NaN"  The string used to represent NaN values.
 21    /// PositiveInfinitySymbol"Infinity" The string used to represent positive
 22    ///                              infinities.
 23    /// NegativeInfinitySymbol"-Infinity" The string used to represent negative
 24    ///                              infinities.
 25    ///
 26    /// Property                  Default  Description
 27    /// CurrencyDecimalSeparator  '.'      The character used as the decimal
 28    ///                                    separator.
 29    /// CurrencyGroupSeparator    ','      The character used to separate groups
 30    ///                                    of digits to the left of the decimal
 31    ///                                    point.
 32    /// CurrencyDecimalDigits     2        The default number of decimal places.
 33    /// CurrencyGroupSizes        3        The number of digits in each group to
 34    ///                                    the left of the decimal point.
 35    /// CurrencyPositivePattern   0        The format of positive values.
 36    /// CurrencyNegativePattern   0        The format of negative values.
 37    /// CurrencySymbol            "$"      String used as local monetary symbol.
 38    /// </remarks>
 39    public sealed class NumberFormatInfo : IFormatProvider, ICloneable
 40    {
 041        internal static readonly string[] s_asciiDigits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
 042        internal static readonly int[] s_intArrayWithElement3 = [3];
 43
 044        internal int[] _numberGroupSizes = s_intArrayWithElement3; // default to [3]; only clones of this array are hand
 045        internal int[] _currencyGroupSizes = s_intArrayWithElement3; // default to [3]; only clones of this array are ha
 046        internal int[] _percentGroupSizes = s_intArrayWithElement3; // default to [3]; only clones of this array are han
 047        internal string _positiveSign = "+";
 048        internal string _negativeSign = "-";
 049        internal string _numberDecimalSeparator = ".";
 050        internal string _numberGroupSeparator = ",";
 051        internal string _currencyGroupSeparator = ",";
 052        internal string _currencyDecimalSeparator = ".";
 053        internal string _currencySymbol = "\x00a4";  // U+00a4 is the symbol for International Monetary Fund.
 054        internal string _nanSymbol = "NaN";
 055        internal string _positiveInfinitySymbol = "Infinity";
 056        internal string _negativeInfinitySymbol = "-Infinity";
 057        internal string _percentDecimalSeparator = ".";
 058        internal string _percentGroupSeparator = ",";
 059        internal string _percentSymbol = "%";
 060        internal string _perMilleSymbol = "\u2030";
 61
 62        internal byte[]? _positiveSignUtf8;
 63        internal byte[]? _negativeSignUtf8;
 64        internal byte[]? _currencySymbolUtf8;
 65        internal byte[]? _numberDecimalSeparatorUtf8;
 66        internal byte[]? _currencyDecimalSeparatorUtf8;
 67        internal byte[]? _currencyGroupSeparatorUtf8;
 68        internal byte[]? _numberGroupSeparatorUtf8;
 69        internal byte[]? _percentSymbolUtf8;
 70        internal byte[]? _percentDecimalSeparatorUtf8;
 71        internal byte[]? _percentGroupSeparatorUtf8;
 72        internal byte[]? _perMilleSymbolUtf8;
 73        internal byte[]? _nanSymbolUtf8;
 74        internal byte[]? _positiveInfinitySymbolUtf8;
 75        internal byte[]? _negativeInfinitySymbolUtf8;
 76
 077        internal string[] _nativeDigits = s_asciiDigits; // default to ASCII digits; only clones of this array are hande
 78
 079        internal int _numberDecimalDigits = 2;
 080        internal int _currencyDecimalDigits = 2;
 81        internal int _currencyPositivePattern;
 82        internal int _currencyNegativePattern;
 083        internal int _numberNegativePattern = 1;
 84        internal int _percentPositivePattern;
 85        internal int _percentNegativePattern;
 086        internal int _percentDecimalDigits = 2;
 87
 088        internal int _digitSubstitution = (int)DigitShapes.None;
 89
 90        internal bool _isReadOnly;
 91
 092        private bool _hasInvariantNumberSigns = true;
 93
 94        // When _allowHyphenDuringParsing is set to true, we'll allow the number parser to accept the hyphen `-` U+002D 
 95        // negative sign is not the hyphen. For example, the Swedish culture (e.g. "sv-SE") has U+2212 as the negative s
 96        private bool _allowHyphenDuringParsing;
 97
 98        // Each UTF-8 cache is allocated one byte longer than the text it holds and the returned span
 99        // excludes that trailing NUL. Consumers should still bound themselves by the span length; the
 100        // terminator only exists so that a helper which scans for one cannot run off the end.
 101        private static ReadOnlySpan<byte> GetUtf8Span(ref byte[]? utf8Cache, string value)
 102        {
 0103            byte[] utf8 = utf8Cache ?? CreateUtf8Cache(ref utf8Cache, value);
 0104            Debug.Assert(utf8.Length > 0);
 0105            return utf8.AsSpan(0, utf8.Length - 1);
 106        }
 107
 108        [MethodImpl(MethodImplOptions.NoInlining)]
 109        private static byte[] CreateUtf8Cache(ref byte[]? utf8Cache, string value)
 110        {
 0111            byte[] utf8 = new byte[Encoding.UTF8.GetByteCount(value) + 1];
 0112            Encoding.UTF8.GetBytes(value, utf8);
 0113            return utf8Cache = utf8;
 114        }
 115
 0116        public NumberFormatInfo()
 117        {
 0118        }
 119
 120        private static void VerifyNativeDigits(string[] nativeDig, string propertyName)
 121        {
 0122            ArgumentNullException.ThrowIfNull(nativeDig);
 123
 0124            if (nativeDig.Length != 10)
 125            {
 0126                throw new ArgumentException(SR.Argument_InvalidNativeDigitCount, propertyName);
 127            }
 128
 0129            for (int i = 0; i < nativeDig.Length; i++)
 130            {
 0131                if (nativeDig[i] == null)
 132                {
 0133                    throw new ArgumentNullException(propertyName, SR.ArgumentNull_ArrayValue);
 134                }
 135
 0136                if (nativeDig[i].Length != 1)
 137                {
 0138                    if (nativeDig[i].Length != 2)
 139                    {
 140                        // Not 1 or 2 UTF-16 code points
 0141                        throw new ArgumentException(SR.Argument_InvalidNativeDigitValue, propertyName);
 142                    }
 0143                    else if (!char.IsSurrogatePair(nativeDig[i][0], nativeDig[i][1]))
 144                    {
 145                        // 2 UTF-6 code points, but not a surrogate pair
 0146                        throw new ArgumentException(SR.Argument_InvalidNativeDigitValue, propertyName);
 147                    }
 148                }
 149
 0150                if (CharUnicodeInfo.GetDecimalDigitValue(nativeDig[i], 0) != i &&
 0151                    CharUnicodeInfo.GetUnicodeCategory(nativeDig[i], 0) != UnicodeCategory.PrivateUse)
 152                {
 153                    // Not the appropriate digit according to the Unicode data properties
 154                    // (Digit 0 must be a 0, etc.).
 0155                    throw new ArgumentException(SR.Argument_InvalidNativeDigitValue, propertyName);
 156                }
 157            }
 0158        }
 159
 160        private static void VerifyDigitSubstitution(DigitShapes digitSub, string propertyName)
 161        {
 162            switch (digitSub)
 163            {
 164                case DigitShapes.Context:
 165                case DigitShapes.None:
 166                case DigitShapes.NativeNational:
 167                    // Success.
 168                    break;
 169
 170                default:
 0171                    throw new ArgumentException(SR.Argument_InvalidDigitSubstitution, propertyName);
 172            }
 0173        }
 174
 0175        internal bool HasInvariantNumberSigns => _hasInvariantNumberSigns;
 0176        internal bool AllowHyphenDuringParsing() => _allowHyphenDuringParsing;
 177
 178        private void InitializeInvariantAndNegativeSignFlags()
 179        {
 0180            _hasInvariantNumberSigns = _positiveSign == "+" && _negativeSign == "-";
 181
 182            // The list of the Minus characters are picked up from the CLDR parse lenient data.
 183            // e.g. https://github.com/unicode-org/cldr/blob/feb602b06bd18ba7333464bd648b68292e8aa54d/common/main/sw.xml
 184
 0185            _allowHyphenDuringParsing = _negativeSign.Length == 1 &&
 0186                                        _negativeSign[0] switch {
 0187                                            '\u2012' or         // Figure Dash
 0188                                            '\u207B' or         // Superscript Minus
 0189                                            '\u208B' or         // Subscript Minus
 0190                                            '\u2212' or         // Minus Sign
 0191                                            '\u2796' or         // Heavy Minus Sign
 0192                                            '\uFE63' or         // Small Hyphen-Minus
 0193                                            '\uFF0D' => true,   // Fullwidth Hyphen-Minus
 0194                                            _ => false
 0195                                        };
 0196        }
 197
 0198        internal NumberFormatInfo(CultureData? cultureData)
 199        {
 0200            if (cultureData != null)
 201            {
 202                // We directly use fields here since these data is coming from data table or Win32, so we
 203                // don't need to verify their values (except for invalid parsing situations).
 0204                cultureData.GetNFIValues(this);
 205
 0206                InitializeInvariantAndNegativeSignFlags();
 207            }
 0208        }
 209
 210        private void VerifyWritable()
 211        {
 0212            if (_isReadOnly)
 213            {
 0214                throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
 215            }
 0216        }
 217
 218        /// <summary>
 219        /// Returns a default NumberFormatInfo that will be universally
 220        /// supported and constant irrespective of the current culture.
 221        /// Used by FromString methods.
 222        /// </summary>
 0223        public static NumberFormatInfo InvariantInfo => field ??=
 0224            // Lazy create the invariant info. This cannot be done in a .cctor because exceptions can
 0225            // be thrown out of a .cctor stack that will need this.
 0226            CultureInfo.InvariantCulture.NumberFormat;
 227
 228        public static NumberFormatInfo GetInstance(IFormatProvider? formatProvider)
 229        {
 0230            return formatProvider == null ?
 0231                CurrentInfo : // Fast path for a null provider
 0232                GetProviderNonNull(formatProvider);
 233
 234            static NumberFormatInfo GetProviderNonNull(IFormatProvider provider)
 235            {
 236                // Fast path for a regular CultureInfo
 0237                if (provider.GetType() == typeof(CultureInfo) && ((CultureInfo)provider)._numInfo is { } info)
 238                {
 0239                    return info;
 240                }
 241
 0242                return
 0243                    provider as NumberFormatInfo ?? // Fast path for an NFI
 0244                    provider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo ??
 0245                    CurrentInfo;
 246            }
 247        }
 248
 249        public object Clone()
 250        {
 0251            NumberFormatInfo n = (NumberFormatInfo)MemberwiseClone();
 0252            n._isReadOnly = false;
 0253            return n;
 254        }
 255
 256        public int CurrencyDecimalDigits
 257        {
 0258            get => _currencyDecimalDigits;
 259            set
 260            {
 0261                if (value < 0 || value > 99)
 262                {
 0263                    ThrowHelper.ThrowArgumentOutOfRange_Range(nameof(value), value, 0, 99);
 264                }
 265
 0266                VerifyWritable();
 0267                _currencyDecimalDigits = value;
 0268            }
 269        }
 270
 271        public string CurrencyDecimalSeparator
 272        {
 0273            get => _currencyDecimalSeparator;
 274            set
 275            {
 0276                VerifyWritable();
 0277                ArgumentException.ThrowIfNullOrEmpty(value);
 0278                _currencyDecimalSeparator = value;
 0279                _currencyDecimalSeparatorUtf8 = null;
 0280            }
 281        }
 282
 283        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 284        internal ReadOnlySpan<TChar> CurrencyDecimalSeparatorTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 285        {
 0286            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0287            return typeof(TChar) == typeof(char) ?
 0288                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_currencyDecimalSeparator) :
 0289                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _currencyDecimalSeparatorUtf8, _
 290        }
 291
 0292        public bool IsReadOnly => _isReadOnly;
 293
 294        /// <summary>
 295        /// Check the values of the groupSize array.
 296        /// Every element in the groupSize array should be between 1 and 9
 297        /// except the last element could be zero.
 298        /// </summary>
 299        internal static void CheckGroupSize(string propName, int[] groupSize)
 300        {
 0301            for (int i = 0; i < groupSize.Length; i++)
 302            {
 0303                if (groupSize[i] < 1)
 304                {
 0305                    if (i == groupSize.Length - 1 && groupSize[i] == 0)
 306                    {
 0307                        return;
 308                    }
 309
 0310                    throw new ArgumentException(SR.Argument_InvalidGroupSize, propName);
 311                }
 0312                else if (groupSize[i] > 9)
 313                {
 0314                    throw new ArgumentException(SR.Argument_InvalidGroupSize, propName);
 315                }
 316            }
 0317        }
 318
 319        public int[] CurrencyGroupSizes
 320        {
 0321            get => (int[])_currencyGroupSizes.Clone();
 322            set
 323            {
 0324                ArgumentNullException.ThrowIfNull(value);
 325
 0326                VerifyWritable();
 327
 0328                int[] inputSizes = (int[])value.Clone();
 0329                CheckGroupSize(nameof(value), inputSizes);
 0330                _currencyGroupSizes = inputSizes;
 0331            }
 332        }
 333
 334        public int[] NumberGroupSizes
 335        {
 0336            get => (int[])_numberGroupSizes.Clone();
 337            set
 338            {
 0339                ArgumentNullException.ThrowIfNull(value);
 340
 0341                VerifyWritable();
 342
 0343                int[] inputSizes = (int[])value.Clone();
 0344                CheckGroupSize(nameof(value), inputSizes);
 0345                _numberGroupSizes = inputSizes;
 0346            }
 347        }
 348
 349        public int[] PercentGroupSizes
 350        {
 0351            get => (int[])_percentGroupSizes.Clone();
 352            set
 353            {
 0354                ArgumentNullException.ThrowIfNull(value);
 355
 0356                VerifyWritable();
 0357                int[] inputSizes = (int[])value.Clone();
 0358                CheckGroupSize(nameof(value), inputSizes);
 0359                _percentGroupSizes = inputSizes;
 0360            }
 361        }
 362
 363        public string CurrencyGroupSeparator
 364        {
 0365            get => _currencyGroupSeparator;
 366            set
 367            {
 0368                VerifyWritable();
 0369                ArgumentNullException.ThrowIfNull(value);
 0370                _currencyGroupSeparator = value;
 0371                _currencyGroupSeparatorUtf8 = null;
 0372            }
 373        }
 374
 375        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 376        internal ReadOnlySpan<TChar> CurrencyGroupSeparatorTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 377        {
 0378            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0379            return typeof(TChar) == typeof(char) ?
 0380                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_currencyGroupSeparator) :
 0381                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _currencyGroupSeparatorUtf8, _cu
 382        }
 383
 384        public string CurrencySymbol
 385        {
 0386            get => _currencySymbol;
 387            set
 388            {
 0389                ArgumentNullException.ThrowIfNull(value);
 390
 0391                VerifyWritable();
 0392                _currencySymbol = value;
 0393                _currencySymbolUtf8 = null;
 0394            }
 395        }
 396
 397        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 398        internal ReadOnlySpan<TChar> CurrencySymbolTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 399        {
 0400            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0401            return typeof(TChar) == typeof(char) ?
 0402                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_currencySymbol) :
 0403                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _currencySymbolUtf8, _currencySy
 404        }
 405
 406        /// <summary>
 407        /// Returns the current culture's NumberFormatInfo. Used by Parse methods.
 408        /// </summary>
 409
 410        public static NumberFormatInfo CurrentInfo
 411        {
 412            get
 413            {
 0414                CultureInfo culture = CultureInfo.CurrentCulture;
 0415                if (!culture._isInherited)
 416                {
 0417                    NumberFormatInfo? info = culture._numInfo;
 0418                    if (info != null)
 419                    {
 0420                        return info;
 421                    }
 422                }
 423                // returns non-nullable when passed typeof(NumberFormatInfo)
 0424                return (NumberFormatInfo)culture.GetFormat(typeof(NumberFormatInfo))!;
 425            }
 426        }
 427
 428        public string NaNSymbol
 429        {
 0430            get => _nanSymbol;
 431            set
 432            {
 0433                ArgumentNullException.ThrowIfNull(value);
 434
 0435                VerifyWritable();
 0436                _nanSymbol = value;
 0437                _nanSymbolUtf8 = null;
 0438            }
 439        }
 440
 441        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 442        internal ReadOnlySpan<TChar> NaNSymbolTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 443        {
 0444            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0445            return typeof(TChar) == typeof(char) ?
 0446                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_nanSymbol) :
 0447                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _nanSymbolUtf8, _nanSymbol));
 448        }
 449
 450        public int CurrencyNegativePattern
 451        {
 0452            get => _currencyNegativePattern;
 453            set
 454            {
 0455                if (value < 0 || value > 16)
 456                {
 0457                    ThrowHelper.ThrowArgumentOutOfRange_Range(nameof(value), value, 0, 16);
 458                }
 459
 0460                VerifyWritable();
 0461                _currencyNegativePattern = value;
 0462            }
 463        }
 464
 465        public int NumberNegativePattern
 466        {
 0467            get => _numberNegativePattern;
 468            set
 469            {
 470                // NOTENOTE: the range of value should correspond to negNumberFormats[] in vm\COMNumber.cpp.
 0471                if (value < 0 || value > 4)
 472                {
 0473                    ThrowHelper.ThrowArgumentOutOfRange_Range(nameof(value), value, 0, 4);
 474                }
 475
 0476                VerifyWritable();
 0477                _numberNegativePattern = value;
 0478            }
 479        }
 480
 481        public int PercentPositivePattern
 482        {
 0483            get => _percentPositivePattern;
 484            set
 485            {
 486                // NOTENOTE: the range of value should correspond to posPercentFormats[] in vm\COMNumber.cpp.
 0487                if (value < 0 || value > 3)
 488                {
 0489                    ThrowHelper.ThrowArgumentOutOfRange_Range(nameof(value), value, 0, 3);
 490                }
 491
 0492                VerifyWritable();
 0493                _percentPositivePattern = value;
 0494            }
 495        }
 496
 497        public int PercentNegativePattern
 498        {
 0499            get => _percentNegativePattern;
 500            set
 501            {
 502                // NOTENOTE: the range of value should correspond to posPercentFormats[] in vm\COMNumber.cpp.
 0503                if (value < 0 || value > 11)
 504                {
 0505                    ThrowHelper.ThrowArgumentOutOfRange_Range(nameof(value), value, 0, 11);
 506                }
 507
 0508                VerifyWritable();
 0509                _percentNegativePattern = value;
 0510            }
 511        }
 512
 513        public string NegativeInfinitySymbol
 514        {
 0515            get => _negativeInfinitySymbol;
 516            set
 517            {
 0518                ArgumentNullException.ThrowIfNull(value);
 519
 0520                VerifyWritable();
 0521                _negativeInfinitySymbol = value;
 0522                _negativeInfinitySymbolUtf8 = null;
 0523            }
 524        }
 525
 526        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 527        internal ReadOnlySpan<TChar> NegativeInfinitySymbolTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 528        {
 0529            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0530            return typeof(TChar) == typeof(char) ?
 0531                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_negativeInfinitySymbol) :
 0532                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _negativeInfinitySymbolUtf8, _ne
 533        }
 534
 535        public string NegativeSign
 536        {
 0537            get => _negativeSign;
 538            set
 539            {
 0540                ArgumentNullException.ThrowIfNull(value);
 541
 0542                VerifyWritable();
 0543                _negativeSign = value;
 0544                _negativeSignUtf8 = null;
 0545                InitializeInvariantAndNegativeSignFlags();
 0546            }
 547        }
 548
 549        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 550        internal ReadOnlySpan<TChar> NegativeSignTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 551        {
 0552            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0553            return typeof(TChar) == typeof(char) ?
 0554                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_negativeSign) :
 0555                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _negativeSignUtf8, _negativeSign
 556        }
 557
 558        public int NumberDecimalDigits
 559        {
 0560            get => _numberDecimalDigits;
 561            set
 562            {
 0563                if (value < 0 || value > 99)
 564                {
 0565                    ThrowHelper.ThrowArgumentOutOfRange_Range(nameof(value), value, 0, 99);
 566                }
 567
 0568                VerifyWritable();
 0569                _numberDecimalDigits = value;
 0570            }
 571        }
 572
 573        public string NumberDecimalSeparator
 574        {
 0575            get => _numberDecimalSeparator;
 576            set
 577            {
 0578                VerifyWritable();
 0579                ArgumentException.ThrowIfNullOrEmpty(value);
 0580                _numberDecimalSeparator = value;
 0581                _numberDecimalSeparatorUtf8 = null;
 0582            }
 583        }
 584
 585        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 586        internal ReadOnlySpan<TChar> NumberDecimalSeparatorTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 587        {
 0588            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0589            return typeof(TChar) == typeof(char) ?
 0590                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_numberDecimalSeparator) :
 0591                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _numberDecimalSeparatorUtf8, _nu
 592        }
 593
 594        public string NumberGroupSeparator
 595        {
 0596            get => _numberGroupSeparator;
 597            set
 598            {
 0599                VerifyWritable();
 0600                ArgumentNullException.ThrowIfNull(value);
 0601                _numberGroupSeparator = value;
 0602                _numberGroupSeparatorUtf8 = null;
 0603            }
 604        }
 605
 606        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 607        internal ReadOnlySpan<TChar> NumberGroupSeparatorTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 608        {
 0609            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0610            return typeof(TChar) == typeof(char) ?
 0611                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_numberGroupSeparator) :
 0612                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _numberGroupSeparatorUtf8, _numb
 613        }
 614
 615        public int CurrencyPositivePattern
 616        {
 0617            get => _currencyPositivePattern;
 618            set
 619            {
 0620                if (value < 0 || value > 3)
 621                {
 0622                    ThrowHelper.ThrowArgumentOutOfRange_Range(nameof(value), value, 0, 3);
 623                }
 624
 0625                VerifyWritable();
 0626                _currencyPositivePattern = value;
 0627            }
 628        }
 629
 630        public string PositiveInfinitySymbol
 631        {
 0632            get => _positiveInfinitySymbol;
 633            set
 634            {
 0635                ArgumentNullException.ThrowIfNull(value);
 636
 0637                VerifyWritable();
 0638                _positiveInfinitySymbol = value;
 0639                _positiveInfinitySymbolUtf8 = null;
 0640            }
 641        }
 642
 643        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 644        internal ReadOnlySpan<TChar> PositiveInfinitySymbolTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 645        {
 0646            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0647            return typeof(TChar) == typeof(char) ?
 0648                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_positiveInfinitySymbol) :
 0649                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _positiveInfinitySymbolUtf8, _po
 650        }
 651
 652        public string PositiveSign
 653        {
 0654            get => _positiveSign;
 655            set
 656            {
 0657                ArgumentNullException.ThrowIfNull(value);
 658
 0659                VerifyWritable();
 0660                _positiveSign = value;
 0661                _positiveSignUtf8 = null;
 0662                InitializeInvariantAndNegativeSignFlags();
 0663            }
 664        }
 665
 666        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 667        internal ReadOnlySpan<TChar> PositiveSignTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 668        {
 0669            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0670            return typeof(TChar) == typeof(char) ?
 0671                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_positiveSign) :
 0672                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _positiveSignUtf8, _positiveSign
 673        }
 674
 675        public int PercentDecimalDigits
 676        {
 0677            get => _percentDecimalDigits;
 678            set
 679            {
 0680                if (value < 0 || value > 99)
 681                {
 0682                    ThrowHelper.ThrowArgumentOutOfRange_Range(nameof(value), value, 0, 99);
 683                }
 684
 0685                VerifyWritable();
 0686                _percentDecimalDigits = value;
 0687            }
 688        }
 689
 690        public string PercentDecimalSeparator
 691        {
 0692            get => _percentDecimalSeparator;
 693            set
 694            {
 0695                VerifyWritable();
 0696                ArgumentException.ThrowIfNullOrEmpty(value);
 0697                _percentDecimalSeparator = value;
 0698                _percentDecimalSeparatorUtf8 = null;
 0699            }
 700        }
 701
 702        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 703        internal ReadOnlySpan<TChar> PercentDecimalSeparatorTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 704        {
 0705            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0706            return typeof(TChar) == typeof(char) ?
 0707                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_percentDecimalSeparator) :
 0708                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _percentDecimalSeparatorUtf8, _p
 709        }
 710
 711        public string PercentGroupSeparator
 712        {
 0713            get => _percentGroupSeparator;
 714            set
 715            {
 0716                VerifyWritable();
 0717                ArgumentNullException.ThrowIfNull(value);
 0718                _percentGroupSeparator = value;
 0719                _percentGroupSeparatorUtf8 = null;
 0720            }
 721        }
 722
 723        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 724        internal ReadOnlySpan<TChar> PercentGroupSeparatorTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 725        {
 0726            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0727            return typeof(TChar) == typeof(char) ?
 0728                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_percentGroupSeparator) :
 0729                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _percentGroupSeparatorUtf8, _per
 730        }
 731
 732        public string PercentSymbol
 733        {
 0734            get => _percentSymbol;
 735            set
 736            {
 0737                ArgumentNullException.ThrowIfNull(value);
 0738                VerifyWritable();
 0739                _percentSymbol = value;
 0740                _percentSymbolUtf8 = null;
 0741            }
 742        }
 743
 744        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 745        internal ReadOnlySpan<TChar> PercentSymbolTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 746        {
 0747            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0748            return typeof(TChar) == typeof(char) ?
 0749                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_percentSymbol) :
 0750                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _percentSymbolUtf8, _percentSymb
 751        }
 752
 753        public string PerMilleSymbol
 754        {
 0755            get => _perMilleSymbol;
 756            set
 757            {
 0758                ArgumentNullException.ThrowIfNull(value);
 759
 0760                VerifyWritable();
 0761                _perMilleSymbol = value;
 0762                _perMilleSymbolUtf8 = null;
 0763            }
 764        }
 765
 766        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 767        internal ReadOnlySpan<TChar> PerMilleSymbolTChar<TChar>() where TChar : unmanaged, IUtfChar<TChar>
 768        {
 0769            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 0770            return typeof(TChar) == typeof(char) ?
 0771                Unsafe.BitCast<ReadOnlySpan<char>, ReadOnlySpan<TChar>>(_perMilleSymbol) :
 0772                Unsafe.BitCast<ReadOnlySpan<byte>, ReadOnlySpan<TChar>>(GetUtf8Span(ref _perMilleSymbolUtf8, _perMilleSy
 773        }
 774
 775        public string[] NativeDigits
 776        {
 0777            get => (string[])_nativeDigits.Clone();
 778            set
 779            {
 0780                VerifyWritable();
 0781                VerifyNativeDigits(value, nameof(value));
 0782                _nativeDigits = value;
 0783            }
 784        }
 785
 786        public DigitShapes DigitSubstitution
 787        {
 0788            get => (DigitShapes)_digitSubstitution;
 789            set
 790            {
 0791                VerifyWritable();
 0792                VerifyDigitSubstitution(value, nameof(value));
 0793                _digitSubstitution = (int)value;
 0794            }
 795        }
 796
 797        public object? GetFormat(Type? formatType)
 798        {
 0799            return formatType == typeof(NumberFormatInfo) ? this : null;
 800        }
 801
 802        public static NumberFormatInfo ReadOnly(NumberFormatInfo nfi)
 803        {
 0804            ArgumentNullException.ThrowIfNull(nfi);
 805
 0806            if (nfi.IsReadOnly)
 807            {
 0808                return nfi;
 809            }
 810
 0811            NumberFormatInfo info = (NumberFormatInfo)(nfi.MemberwiseClone());
 0812            info._isReadOnly = true;
 0813            return info;
 814        }
 815
 816        // private const NumberStyles InvalidNumberStyles = unchecked((NumberStyles) 0xFFFFF800);
 817        private const NumberStyles InvalidNumberStyles = ~(NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWh
 818                                                           | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingS
 819                                                           | NumberStyles.AllowParentheses | NumberStyles.AllowDecimalPo
 820                                                           | NumberStyles.AllowThousands | NumberStyles.AllowExponent
 821                                                           | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpe
 822                                                           | NumberStyles.AllowBinarySpecifier);
 823
 824        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 825        internal static void ValidateParseStyleInteger(NumberStyles style)
 826        {
 827            // Check for undefined flags or using AllowHexSpecifier/AllowBinarySpecifier each with anything other than A
 0828            if ((style & (InvalidNumberStyles | NumberStyles.AllowHexSpecifier | NumberStyles.AllowBinarySpecifier)) != 
 0829                (style & ~NumberStyles.HexNumber) != 0 &&
 0830                (style & ~NumberStyles.BinaryNumber) != 0)
 831            {
 0832                ThrowInvalid(style);
 833
 834                static void ThrowInvalid(NumberStyles value)
 835                {
 0836                    throw new ArgumentException(
 0837                        (value & InvalidNumberStyles) != 0 ? SR.Argument_InvalidNumberStyles : SR.Arg_InvalidHexBinarySt
 0838                        nameof(style));
 839                }
 840            }
 0841        }
 842
 843        internal static void ValidateParseStyleFloatingPoint(NumberStyles style)
 844        {
 845            // Check for undefined flags, AllowBinarySpecifier (never valid for float), or AllowHexSpecifier with anythi
 846            // When AllowHexSpecifier is specified, AllowExponent must also be specified; this reserves
 847            // AllowHexSpecifier without AllowExponent for possible future use (e.g. optional p exponent).
 0848            if ((style & (InvalidNumberStyles | NumberStyles.AllowBinarySpecifier | NumberStyles.AllowHexSpecifier)) != 
 0849                ((style & ~NumberStyles.HexFloat) != 0 ||
 0850                 (style & NumberStyles.AllowHexSpecifier) != 0 && (style & NumberStyles.AllowExponent) == 0))
 851            {
 0852                ThrowInvalid(style);
 853
 854                static void ThrowInvalid(NumberStyles value)
 855                {
 0856                    throw new ArgumentException(
 0857                        (value & InvalidNumberStyles) != 0 ? SR.Argument_InvalidNumberStyles :
 0858                        (value & NumberStyles.AllowBinarySpecifier) != 0 ? SR.Arg_BinaryStyleNotSupported :
 0859                        SR.Arg_InvalidHexFloatStyle,
 0860                        nameof(style));
 861                }
 862            }
 0863        }
 864
 865        internal static void ValidateParseStyleDecimal(NumberStyles style)
 866        {
 867            // Check for undefined flags or hex/binary specifiers.
 0868            if ((style & (InvalidNumberStyles | NumberStyles.AllowHexSpecifier | NumberStyles.AllowBinarySpecifier)) != 
 869            {
 0870                ThrowInvalid(style);
 871
 872                static void ThrowInvalid(NumberStyles value) =>
 0873                    throw new ArgumentException((value & InvalidNumberStyles) != 0 ? SR.Argument_InvalidNumberStyles : S
 874            }
 0875        }
 876    }
 877}
 878

Methods/Properties

.cctor()
.ctor()
GetUtf8Span(System.Byte[]&,System.String)
CreateUtf8Cache(System.Byte[]&,System.String)
VerifyNativeDigits(System.String[],System.String)
VerifyDigitSubstitution(System.Globalization.DigitShapes,System.String)
HasInvariantNumberSigns()
AllowHyphenDuringParsing()
InitializeInvariantAndNegativeSignFlags()
.ctor(System.Globalization.CultureData)
VerifyWritable()
InvariantInfo()
GetInstance(System.IFormatProvider)
GetProviderNonNull(System.IFormatProvider)
Clone()
CurrencyDecimalDigits()
CurrencyDecimalDigits(System.Int32)
CurrencyDecimalSeparator()
CurrencyDecimalSeparator(System.String)
CurrencyDecimalSeparatorTChar()
IsReadOnly()
CheckGroupSize(System.String,System.Int32[])
CurrencyGroupSizes()
CurrencyGroupSizes(System.Int32[])
NumberGroupSizes()
NumberGroupSizes(System.Int32[])
PercentGroupSizes()
PercentGroupSizes(System.Int32[])
CurrencyGroupSeparator()
CurrencyGroupSeparator(System.String)
CurrencyGroupSeparatorTChar()
CurrencySymbol()
CurrencySymbol(System.String)
CurrencySymbolTChar()
CurrentInfo()
NaNSymbol()
NaNSymbol(System.String)
NaNSymbolTChar()
CurrencyNegativePattern()
CurrencyNegativePattern(System.Int32)
NumberNegativePattern()
NumberNegativePattern(System.Int32)
PercentPositivePattern()
PercentPositivePattern(System.Int32)
PercentNegativePattern()
PercentNegativePattern(System.Int32)
NegativeInfinitySymbol()
NegativeInfinitySymbol(System.String)
NegativeInfinitySymbolTChar()
NegativeSign()
NegativeSign(System.String)
NegativeSignTChar()
NumberDecimalDigits()
NumberDecimalDigits(System.Int32)
NumberDecimalSeparator()
NumberDecimalSeparator(System.String)
NumberDecimalSeparatorTChar()
NumberGroupSeparator()
NumberGroupSeparator(System.String)
NumberGroupSeparatorTChar()
CurrencyPositivePattern()
CurrencyPositivePattern(System.Int32)
PositiveInfinitySymbol()
PositiveInfinitySymbol(System.String)
PositiveInfinitySymbolTChar()
PositiveSign()
PositiveSign(System.String)
PositiveSignTChar()
PercentDecimalDigits()
PercentDecimalDigits(System.Int32)
PercentDecimalSeparator()
PercentDecimalSeparator(System.String)
PercentDecimalSeparatorTChar()
PercentGroupSeparator()
PercentGroupSeparator(System.String)
PercentGroupSeparatorTChar()
PercentSymbol()
PercentSymbol(System.String)
PercentSymbolTChar()
PerMilleSymbol()
PerMilleSymbol(System.String)
PerMilleSymbolTChar()
NativeDigits()
NativeDigits(System.String[])
DigitSubstitution()
DigitSubstitution(System.Globalization.DigitShapes)
GetFormat(System.Type)
ReadOnly(System.Globalization.NumberFormatInfo)
ValidateParseStyleInteger(System.Globalization.NumberStyles)
ThrowInvalid(System.Globalization.NumberStyles)
ValidateParseStyleFloatingPoint(System.Globalization.NumberStyles)
ThrowInvalid(System.Globalization.NumberStyles)
ValidateParseStyleDecimal(System.Globalization.NumberStyles)
ThrowInvalid(System.Globalization.NumberStyles)