11// Licensed under MIT No Attribution, see LICENSE file at the root.
22// Copyright 2013 Andreas Gullberg Larsen ([email protected] ). Maintained at https://github.com/angularsen/UnitsNet. 33
4-
4+ using System ;
5+ using System . Diagnostics ;
6+ using System . Runtime . InteropServices ;
7+ using System . Text ;
58using UnitsNet . InternalHelpers ;
69
710namespace UnitsNet
@@ -16,35 +19,49 @@ namespace UnitsNet
1619 /// </list>
1720 /// </summary>
1821 /// <remarks>
19- /// At the time of this writing, this reduces the number of From(value, unit) overloads to 1/4th:
22+ /// <para> At the time of this writing, this reduces the number of From(value, unit) overloads to 1/4th:
2023 /// From 8 (int, long, double, decimal + each nullable) down to 2 (QuantityValue and QuantityValue?).
21- /// This also adds more numeric types with no extra overhead, such as float, short and byte.
24+ /// This also adds more numeric types with no extra overhead, such as float, short and byte.</para>
25+ /// <para>So far, the internal representation can be either <see cref="double"/> or <see cref="decimal"/>,
26+ /// but as this struct is realized as a union struct with overlapping fields, only the amount of memory of the largest data type is used.
27+ /// This allows for adding support for smaller data types without increasing the overall size.</para>
2228 /// </remarks>
23- public struct QuantityValue
29+ [ StructLayout ( LayoutKind . Explicit ) ]
30+ [ DebuggerDisplay ( "{GetDebugRepresentation()}" ) ]
31+ public readonly struct QuantityValue
2432 {
2533 /// <summary>
2634 /// Value assigned when implicitly casting from all numeric types except <see cref="decimal" />, since
27- /// <see cref="double" /> has the greatest range and is 64 bits versus 128 bits for <see cref="decimal"/> .
35+ /// <see cref="double" /> has the greatest range.
2836 /// </summary>
29- private readonly double ? _value ;
37+ [ FieldOffset ( 8 ) ] // so that it does not interfere with the Type field
38+ private readonly double _doubleValue ;
3039
3140 /// <summary>
3241 /// Value assigned when implicitly casting from <see cref="decimal" /> type, since it has a greater precision than
3342 /// <see cref="double"/> and we want to preserve that when constructing quantities that use <see cref="decimal"/>
3443 /// as their value type.
3544 /// </summary>
36- private readonly decimal ? _valueDecimal ;
45+ [ FieldOffset ( 0 ) ]
46+ // bytes layout: 0-1 unused, 2 exponent, 3 sign (only highest bit), 4-15 number
47+ private readonly decimal _decimalValue ;
48+
49+ /// <summary>
50+ /// Determines the underlying type of this <see cref="QuantityValue"/>.
51+ /// </summary>
52+ [ FieldOffset ( 0 ) ] // using unused byte for storing type
53+ public readonly UnderlyingDataType Type ;
3754
38- private QuantityValue ( double val )
55+ private QuantityValue ( double val ) : this ( )
3956 {
40- _value = Guard . EnsureValidNumber ( val , nameof ( val ) ) ;
41- _valueDecimal = null ;
57+ _doubleValue = Guard . EnsureValidNumber ( val , nameof ( val ) ) ;
58+ Type = UnderlyingDataType . Double ;
4259 }
4360
44- private QuantityValue ( decimal val )
61+ private QuantityValue ( decimal val ) : this ( )
4562 {
46- _valueDecimal = val ;
47- _value = null ;
63+ _decimalValue = val ;
64+ Type = UnderlyingDataType . Decimal ;
4865 }
4966
5067 #region To QuantityValue
@@ -72,28 +89,59 @@ private QuantityValue(decimal val)
7289
7390 /// <summary>Explicit cast from <see cref="QuantityValue"/> to <see cref="double"/>.</summary>
7491 public static explicit operator double ( QuantityValue number )
75- {
76- // double -> decimal -> zero (since we can't implement the default struct ctor)
77- return number . _value ?? ( double ) number . _valueDecimal . GetValueOrDefault ( ) ;
78- }
92+ => number . Type switch
93+ {
94+ UnderlyingDataType . Decimal => ( double ) number . _decimalValue ,
95+ UnderlyingDataType . Double => number . _doubleValue ,
96+ _ => throw new NotImplementedException ( )
97+ } ;
7998
8099 #endregion
81100
82101 #region To decimal
83102
84103 /// <summary>Explicit cast from <see cref="QuantityValue"/> to <see cref="decimal"/>.</summary>
85104 public static explicit operator decimal ( QuantityValue number )
86- {
87- // decimal -> double -> zero (since we can't implement the default struct ctor)
88- return number . _valueDecimal ?? ( decimal ) number . _value . GetValueOrDefault ( ) ;
89- }
105+ => number . Type switch
106+ {
107+ UnderlyingDataType . Decimal => number . _decimalValue ,
108+ UnderlyingDataType . Double => ( decimal ) number . _doubleValue ,
109+ _ => throw new NotImplementedException ( )
110+ } ;
90111
91112 #endregion
92113
93114 /// <summary>Returns the string representation of the numeric value.</summary>
94115 public override string ToString ( )
116+ => Type switch
117+ {
118+ UnderlyingDataType . Decimal => _decimalValue . ToString ( ) ,
119+ UnderlyingDataType . Double => _doubleValue . ToString ( ) ,
120+ _ => throw new NotImplementedException ( )
121+ } ;
122+
123+ private string GetDebugRepresentation ( )
124+ {
125+ StringBuilder builder = new ( $ "{ Type } { ToString ( ) } Hex:") ;
126+
127+ byte [ ] bytes = BytesUtility . GetBytes ( this ) ;
128+ for ( int i = bytes . Length - 1 ; i >= 0 ; i -- )
129+ {
130+ builder . Append ( $ " { bytes [ i ] : X2} ") ;
131+ }
132+
133+ return builder . ToString ( ) ;
134+ }
135+
136+ /// <summary>
137+ /// Describes the underlying type of a <see cref="QuantityValue"/>.
138+ /// </summary>
139+ public enum UnderlyingDataType : byte
95140 {
96- return _value . HasValue ? _value . ToString ( ) : _valueDecimal . ToString ( ) ;
141+ /// <summary><see cref="Decimal"/> must have the value 0 due to the bit structure of <see cref="decimal"/>.</summary>
142+ Decimal = 0 ,
143+ /// <inheritdoc cref="double"/>
144+ Double = 1
97145 }
98146 }
99147}
0 commit comments