Skip to content

Commit f748ad2

Browse files
tmilnthorpangularsen
authored andcommitted
Avoid reflection in InfosLazy (#631)
* Avoid reflection in InfosLazy * Moving non-generated code out of Quantity.g.cs to Quantity.cs
1 parent 1813011 commit f748ad2

File tree

5 files changed

+541
-378
lines changed

5 files changed

+541
-378
lines changed

UnitsNet.Tests/DummyIQuantity.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using JetBrains.Annotations;
5+
6+
namespace UnitsNet.Tests
7+
{
8+
internal class DummyIQuantity : IQuantity
9+
{
10+
public QuantityType Type => throw new NotImplementedException();
11+
12+
public BaseDimensions Dimensions => throw new NotImplementedException();
13+
14+
public QuantityInfo QuantityInfo => throw new NotImplementedException();
15+
16+
public Enum Unit => throw new NotImplementedException();
17+
18+
public double Value => throw new NotImplementedException();
19+
20+
public double As(Enum unit)
21+
{
22+
throw new NotImplementedException();
23+
}
24+
25+
public double As(UnitSystem unitSystem)
26+
{
27+
throw new NotImplementedException();
28+
}
29+
30+
public string ToString([CanBeNull] IFormatProvider provider)
31+
{
32+
throw new NotImplementedException();
33+
}
34+
35+
public string ToString([CanBeNull] IFormatProvider provider, int significantDigitsAfterRadix)
36+
{
37+
throw new NotImplementedException();
38+
}
39+
40+
public string ToString([CanBeNull] IFormatProvider provider, [NotNull] string format, [NotNull] params object[] args)
41+
{
42+
throw new NotImplementedException();
43+
}
44+
45+
public string ToString(string format, IFormatProvider formatProvider)
46+
{
47+
throw new NotImplementedException();
48+
}
49+
50+
public IQuantity ToUnit(Enum unit)
51+
{
52+
throw new NotImplementedException();
53+
}
54+
55+
public IQuantity ToUnit(UnitSystem unitSystem)
56+
{
57+
throw new NotImplementedException();
58+
}
59+
}
60+
}

UnitsNet.Tests/QuantityTest.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Globalization;
66
using System.Linq;
7+
using JetBrains.Annotations;
78
using UnitsNet.Units;
89
using Xunit;
910

@@ -135,6 +136,13 @@ public void TryFrom_GivenValueAndUnit_ReturnsQuantity()
135136
Assert.Equal(Pressure.FromMegabars(3), parsedPressure);
136137
}
137138

139+
[Fact]
140+
public void TryParse_GivenInvalidQuantityType_ReturnsFalseAndNullQuantity()
141+
{
142+
Assert.False(Quantity.TryParse(typeof(DummyIQuantity), "3.0 cm", out IQuantity parsedLength));
143+
Assert.Null(parsedLength);
144+
}
145+
138146
[Fact]
139147
public void TryParse_GivenInvalidString_ReturnsFalseAndNullQuantity()
140148
{
@@ -171,5 +179,27 @@ public void Types_ReturnsKnownQuantityTypes()
171179
Assert.Superset(knownQuantities.ToHashSet(), types.ToHashSet());
172180
Assert.Equal(QuantityCount, types.Length);
173181
}
182+
183+
[Fact]
184+
public void FromQuantityType_GivenUndefinedQuantityType_ThrowsArgumentException()
185+
{
186+
Assert.Throws<ArgumentException>(() => Quantity.FromQuantityType(QuantityType.Undefined, 0.0));
187+
}
188+
189+
[Fact]
190+
public void FromQuantityType_GivenInvalidQuantityType_ThrowsArgumentException()
191+
{
192+
Assert.Throws<ArgumentException>(() => Quantity.FromQuantityType((QuantityType)(-1), 0.0));
193+
}
194+
195+
[Fact]
196+
public void FromQuantityType_GivenLengthQuantityType_ReturnsLengthQuantity()
197+
{
198+
var fromQuantity = Quantity.FromQuantityType(QuantityType.Length, 0.0);
199+
200+
Assert.Equal(0.0, fromQuantity.Value);
201+
Assert.Equal(QuantityType.Length, fromQuantity.Type);
202+
Assert.Equal(Length.BaseUnit, fromQuantity.Unit);
203+
}
174204
}
175205
}

UnitsNet/CustomCode/Quantity.cs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.Linq;
5+
using JetBrains.Annotations;
46
using UnitsNet.InternalHelpers;
57

68
namespace UnitsNet
@@ -15,17 +17,9 @@ static Quantity()
1517
Types = quantityTypes;
1618
Names = quantityTypes.Select(qt => qt.ToString()).ToArray();
1719

18-
// A bunch of reflection to enumerate quantity types, instantiate with the default constructor and return its QuantityInfo property
19-
InfosLazy = new Lazy<QuantityInfo[]>(() => typeof(Length)
20-
.Wrap()
21-
.Assembly
22-
.GetExportedTypes()
23-
.Where(typeof(IQuantity).IsAssignableFrom)
24-
.Where(t => t.Wrap().IsClass || t.Wrap().IsValueType) // Future-proofing: Considering changing quantities from struct to class
25-
.Select(Activator.CreateInstance)
26-
.Cast<IQuantity>()
27-
.Select(q => q.QuantityInfo)
28-
.OrderBy(q => q.Name)
20+
InfosLazy = new Lazy<QuantityInfo[]>(() => Types
21+
.Select(quantityType => FromQuantityType(quantityType, 0.0).QuantityInfo)
22+
.OrderBy(quantityInfo => quantityInfo.Name)
2923
.ToArray());
3024
}
3125

@@ -60,6 +54,55 @@ public static IQuantity From(QuantityValue value, Enum unit)
6054
$"Unit value {unit} of type {unit.GetType()} is not a known unit enum type. Expected types like UnitsNet.Units.LengthUnit. Did you pass in a third-party enum type defined outside UnitsNet library?");
6155
}
6256

57+
/// <inheritdoc cref="TryFrom(QuantityValue,System.Enum,out UnitsNet.IQuantity)"/>
58+
public static bool TryFrom(double value, Enum unit, out IQuantity quantity)
59+
{
60+
// Implicit cast to QuantityValue would prevent TryFrom from being called,
61+
// so we need to explicitly check this here for double arguments.
62+
if (double.IsNaN(value) || double.IsInfinity(value))
63+
{
64+
quantity = default(IQuantity);
65+
return false;
66+
}
67+
68+
return TryFrom((QuantityValue)value, unit, out quantity);
69+
}
70+
71+
/// <inheritdoc cref="Parse(IFormatProvider, System.Type,string)"/>
72+
public static IQuantity Parse(Type quantityType, string quantityString) => Parse(null, quantityType, quantityString);
73+
74+
/// <summary>
75+
/// Dynamically parse a quantity string representation.
76+
/// </summary>
77+
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="CultureInfo.CurrentUICulture" /> if null.</param>
78+
/// <param name="quantityType">Type of quantity, such as <see cref="Length"/>.</param>
79+
/// <param name="quantityString">Quantity string representation, such as "1.5 kg". Must be compatible with given quantity type.</param>
80+
/// <returns>The parsed quantity.</returns>
81+
/// <exception cref="ArgumentException">Type must be of type UnitsNet.IQuantity -or- Type is not a known quantity type.</exception>
82+
public static IQuantity Parse([CanBeNull] IFormatProvider formatProvider, Type quantityType, string quantityString)
83+
{
84+
if (!typeof(IQuantity).Wrap().IsAssignableFrom(quantityType))
85+
throw new ArgumentException($"Type {quantityType} must be of type UnitsNet.IQuantity.");
86+
87+
if (TryParse(formatProvider, quantityType, quantityString, out IQuantity quantity)) return quantity;
88+
89+
throw new ArgumentException($"Quantity string could not be parsed to quantity {quantityType}.");
90+
}
91+
92+
/// <inheritdoc cref="TryParse(IFormatProvider,System.Type,string,out UnitsNet.IQuantity)"/>
93+
public static bool TryParse(Type quantityType, string quantityString, out IQuantity quantity) =>
94+
TryParse(null, quantityType, quantityString, out quantity);
95+
96+
/// <summary>
97+
/// Get information about the given quantity type.
98+
/// </summary>
99+
/// <param name="quantityType">The quantity type enum value.</param>
100+
/// <returns>Information about the quantity and its units.</returns>
101+
public static QuantityInfo GetInfo(QuantityType quantityType)
102+
{
103+
return Infos.First(qi => qi.QuantityType == quantityType);
104+
}
105+
63106
/// <summary>
64107
/// Get a list of quantities that has the given base dimensions.
65108
/// </summary>

0 commit comments

Comments
 (0)