Skip to content

Commit 48b2e88

Browse files
authored
✨ Improve nullable annotations for net7.0 multi-targeting (#1175)
Inspired by article: https://www.meziantou.net/how-to-use-nullable-reference-types-in-dotnet-standard-2-0-and-dotnet-.htm Related to #1164 ### Changes - Add `NullableAttributes.cs` via `Directory.Build.props` to shim annotation attributes for netstandard2.0 and older .NET versions - Fix nullable warnings, observed when multitargeting net7.0 + netstandard2.0 - Add `[NotNullWhen(true)]` to all `Try` methods - Remove redundant `!` nullable suppressions
1 parent d53ea95 commit 48b2e88

File tree

131 files changed

+4010
-2851
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

131 files changed

+4010
-2851
lines changed

CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public string Generate()
3737
Writer.WL(GeneratedFileHeader);
3838
Writer.WL(@"
3939
using System;
40+
using System.Diagnostics.CodeAnalysis;
4041
using System.Globalization;
4142
using System.Linq;
4243
using System.Runtime.Serialization;
@@ -761,7 +762,7 @@ private void GenerateEqualityAndComparison()
761762
/// <summary>Indicates strict equality of two <see cref=""{_quantity.Name}""/> quantities, where both <see cref=""Value"" /> and <see cref=""Unit"" /> are exactly equal.</summary>
762763
/// <remarks>Consider using <see cref=""Equals({_quantity.Name}, {_valueType}, ComparisonType)""/> to check equality across different units and to specify a floating-point number error tolerance.</remarks>
763764
[Obsolete(""Consider using Equals(Angle, {_valueType}, ComparisonType) to check equality across different units and to specify a floating-point number error tolerance."")]
764-
public override bool Equals(object obj)
765+
public override bool Equals(object? obj)
765766
{{
766767
if (obj is null || !(obj is {_quantity.Name} otherQuantity))
767768
return false;
@@ -793,7 +794,7 @@ public bool Equals({_quantity.Name} other)
793794
/// <item><term> Greater than zero</term><description> This instance follows <paramref name=""obj"" /> in the sort order.</description></item>
794795
/// </list>
795796
/// </returns>
796-
public int CompareTo(object obj)
797+
public int CompareTo(object? obj)
797798
{{
798799
if (obj is null) throw new ArgumentNullException(nameof(obj));
799800
if (!(obj is {_quantity.Name} otherQuantity)) throw new ArgumentException(""Expected type {_quantity.Name}."", nameof(obj));
@@ -995,15 +996,15 @@ double IQuantity.As(Enum unit)
995996
/// <param name=""unit"">The unit to convert to.</param>
996997
/// <param name=""converted"">The converted <see cref=""{_quantity.Name}""/> in <paramref name=""unit""/>, if successful.</param>
997998
/// <returns>True if successful, otherwise false.</returns>
998-
private bool TryToUnit({_quantity.Name}Unit unit, out {_quantity.Name}? converted)
999+
private bool TryToUnit({_quantity.Name}Unit unit, [NotNullWhen(true)] out {_quantity.Name}? converted)
9991000
{{
10001001
if (Unit == unit)
10011002
{{
10021003
converted = this;
10031004
return true;
10041005
}}
10051006
1006-
converted = (Unit, unit) switch
1007+
{_quantity.Name}? convertedOrNull = (Unit, unit) switch
10071008
{{
10081009
// {_quantity.Name}Unit -> BaseUnit");
10091010

@@ -1031,10 +1032,17 @@ private bool TryToUnit({_quantity.Name}Unit unit, out {_quantity.Name}? converte
10311032

10321033
Writer.WL();
10331034
Writer.WL($@"
1034-
_ => null!
1035+
_ => null
10351036
}};
10361037
1037-
return converted is not null;
1038+
if (convertedOrNull is null)
1039+
{{
1040+
converted = default;
1041+
return false;
1042+
}}
1043+
1044+
converted = convertedOrNull.Value;
1045+
return true;
10381046
}}
10391047
10401048
/// <inheritdoc />
@@ -1104,7 +1112,7 @@ public string ToString(IFormatProvider? provider)
11041112
/// </summary>
11051113
/// <param name=""format"">The format string.</param>
11061114
/// <returns>The string representation.</returns>
1107-
public string ToString(string format)
1115+
public string ToString(string? format)
11081116
{{
11091117
return ToString(format, CultureInfo.CurrentCulture);
11101118
}}
@@ -1116,7 +1124,7 @@ public string ToString(string format)
11161124
/// <param name=""format"">The format string.</param>
11171125
/// <param name=""provider"">Format to use for localization and number formatting. Defaults to <see cref=""CultureInfo.CurrentCulture"" /> if null.</param>
11181126
/// <returns>The string representation.</returns>
1119-
public string ToString(string format, IFormatProvider? provider)
1127+
public string ToString(string? format, IFormatProvider? provider)
11201128
{{
11211129
return QuantityFormatter.Format<{_unitEnumName}>(this, format, provider);
11221130
}}
@@ -1127,75 +1135,75 @@ public string ToString(string format, IFormatProvider? provider)
11271135

11281136
private void GenerateIConvertibleMethods()
11291137
{
1130-
Writer.WL($@"
1138+
Writer.WL($@"
11311139
#region IConvertible Methods
11321140
11331141
TypeCode IConvertible.GetTypeCode()
11341142
{{
11351143
return TypeCode.Object;
11361144
}}
11371145
1138-
bool IConvertible.ToBoolean(IFormatProvider provider)
1146+
bool IConvertible.ToBoolean(IFormatProvider? provider)
11391147
{{
11401148
throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to bool is not supported."");
11411149
}}
11421150
1143-
byte IConvertible.ToByte(IFormatProvider provider)
1151+
byte IConvertible.ToByte(IFormatProvider? provider)
11441152
{{
11451153
return Convert.ToByte(_value);
11461154
}}
11471155
1148-
char IConvertible.ToChar(IFormatProvider provider)
1156+
char IConvertible.ToChar(IFormatProvider? provider)
11491157
{{
11501158
throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to char is not supported."");
11511159
}}
11521160
1153-
DateTime IConvertible.ToDateTime(IFormatProvider provider)
1161+
DateTime IConvertible.ToDateTime(IFormatProvider? provider)
11541162
{{
11551163
throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to DateTime is not supported."");
11561164
}}
11571165
1158-
decimal IConvertible.ToDecimal(IFormatProvider provider)
1166+
decimal IConvertible.ToDecimal(IFormatProvider? provider)
11591167
{{
11601168
return Convert.ToDecimal(_value);
11611169
}}
11621170
1163-
double IConvertible.ToDouble(IFormatProvider provider)
1171+
double IConvertible.ToDouble(IFormatProvider? provider)
11641172
{{
11651173
return Convert.ToDouble(_value);
11661174
}}
11671175
1168-
short IConvertible.ToInt16(IFormatProvider provider)
1176+
short IConvertible.ToInt16(IFormatProvider? provider)
11691177
{{
11701178
return Convert.ToInt16(_value);
11711179
}}
11721180
1173-
int IConvertible.ToInt32(IFormatProvider provider)
1181+
int IConvertible.ToInt32(IFormatProvider? provider)
11741182
{{
11751183
return Convert.ToInt32(_value);
11761184
}}
11771185
1178-
long IConvertible.ToInt64(IFormatProvider provider)
1186+
long IConvertible.ToInt64(IFormatProvider? provider)
11791187
{{
11801188
return Convert.ToInt64(_value);
11811189
}}
11821190
1183-
sbyte IConvertible.ToSByte(IFormatProvider provider)
1191+
sbyte IConvertible.ToSByte(IFormatProvider? provider)
11841192
{{
11851193
return Convert.ToSByte(_value);
11861194
}}
11871195
1188-
float IConvertible.ToSingle(IFormatProvider provider)
1196+
float IConvertible.ToSingle(IFormatProvider? provider)
11891197
{{
11901198
return Convert.ToSingle(_value);
11911199
}}
11921200
1193-
string IConvertible.ToString(IFormatProvider provider)
1201+
string IConvertible.ToString(IFormatProvider? provider)
11941202
{{
11951203
return ToString(""g"", provider);
11961204
}}
11971205
1198-
object IConvertible.ToType(Type conversionType, IFormatProvider provider)
1206+
object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
11991207
{{
12001208
if (conversionType == typeof({_quantity.Name}))
12011209
return this;
@@ -1209,17 +1217,17 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
12091217
throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to {{conversionType}} is not supported."");
12101218
}}
12111219
1212-
ushort IConvertible.ToUInt16(IFormatProvider provider)
1220+
ushort IConvertible.ToUInt16(IFormatProvider? provider)
12131221
{{
12141222
return Convert.ToUInt16(_value);
12151223
}}
12161224
1217-
uint IConvertible.ToUInt32(IFormatProvider provider)
1225+
uint IConvertible.ToUInt32(IFormatProvider? provider)
12181226
{{
12191227
return Convert.ToUInt32(_value);
12201228
}}
12211229
1222-
ulong IConvertible.ToUInt64(IFormatProvider provider)
1230+
ulong IConvertible.ToUInt64(IFormatProvider? provider)
12231231
{{
12241232
return Convert.ToUInt64(_value);
12251233
}}

CodeGen/Generators/UnitsNetGen/StaticQuantityGenerator.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public string Generate()
2020
using System.Globalization;
2121
using UnitsNet.Units;
2222
using System.Collections.Generic;
23+
using System.Diagnostics.CodeAnalysis;
2324
2425
#nullable enable
2526
@@ -70,7 +71,7 @@ public static IQuantity FromQuantityInfo(QuantityInfo quantityInfo, QuantityValu
7071
/// <param name=""unit"">Unit enum value.</param>
7172
/// <param name=""quantity"">The resulting quantity if successful, otherwise <c>default</c>.</param>
7273
/// <returns><c>True</c> if successful with <paramref name=""quantity""/> assigned the value, otherwise <c>false</c>.</returns>
73-
public static bool TryFrom(QuantityValue value, Enum unit, out IQuantity? quantity)
74+
public static bool TryFrom(QuantityValue value, Enum unit, [NotNullWhen(true)] out IQuantity? quantity)
7475
{
7576
switch (unit)
7677
{");
@@ -102,7 +103,7 @@ public static bool TryFrom(QuantityValue value, Enum unit, out IQuantity? quanti
102103
/// <param name=""quantityString"">Quantity string representation, such as ""1.5 kg"". Must be compatible with given quantity type.</param>
103104
/// <param name=""quantity"">The resulting quantity if successful, otherwise <c>default</c>.</param>
104105
/// <returns>The parsed quantity.</returns>
105-
public static bool TryParse(IFormatProvider? formatProvider, Type quantityType, string quantityString, out IQuantity? quantity)
106+
public static bool TryParse(IFormatProvider? formatProvider, Type quantityType, string quantityString, [NotNullWhen(true)] out IQuantity? quantity)
106107
{
107108
quantity = default(IQuantity);
108109

Directory.Build.props

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<!-- This file will be implicitly linked by all projects in folder -folders -->
33
<PropertyGroup>
44
<!-- Default to output to Artifacts folder -->
5-
<OutputPath>$(MSBuildThisFileDirectory)Artifacts/$(MSBuildProjectName)</OutputPath>
5+
<OutputPath>$(MSBuildThisFileDirectory)Artifacts/$(MSBuildProjectName)</OutputPath>
66
<!-- Specific output folder for .NET nanoFramework projects -->
7-
<OutputPath Condition=" '$(TargetFrameworkIdentifier)' == '.NETnanoFramework'">$(MSBuildThisFileDirectory)Artifacts/UnitsNet.NanoFramework/$(MSBuildProjectName)</OutputPath>
7+
<OutputPath Condition=" '$(TargetFrameworkIdentifier)' == '.NETnanoFramework'">$(MSBuildThisFileDirectory)Artifacts/UnitsNet.NanoFramework/$(MSBuildProjectName)</OutputPath>
88

99
</PropertyGroup>
1010

@@ -17,6 +17,12 @@
1717
<PropertyGroup>
1818
<!-- Warning instead of compile error on obsolete errors.-->
1919
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
20+
<!-- 612: obsolete, 618: obsolete with message -->
2021
<WarningsNotAsErrors>612,618</WarningsNotAsErrors>
2122
</PropertyGroup>
23+
24+
<ItemGroup>
25+
<Compile Include="$(MSBuildThisFileDirectory)/NullableAttributes.cs" />
26+
</ItemGroup>
27+
2228
</Project>

0 commit comments

Comments
 (0)