Skip to content

Commit c7e1b18

Browse files
committed
Conversion of unions from/to a value can be controlled via attribute.
1 parent 45897c6 commit c7e1b18

File tree

35 files changed

+1261
-83
lines changed

35 files changed

+1261
-83
lines changed

samples/Thinktecture.Runtime.Extensions.Samples/Unions/TextOrNumberWithAdditionalProperty.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace Thinktecture.Unions;
22

33
[Union<string, int>(T1Name = "Text",
44
T2Name = "Number",
5-
SkipImplicitConversionFromValue = true,
5+
ConversionFromValue = ConversionOperatorsGeneration.None,
66
ConstructorAccessModifier = UnionConstructorAccessModifier.Private)]
77
public partial class TextOrNumberExtended
88
{

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionCodeGenerator.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,8 @@ private void GenerateUnion(CancellationToken cancellationToken)
114114
GenerateMap(true);
115115
}
116116

117-
if (!_state.Settings.SkipImplicitConversionFromValue)
118-
GenerateImplicitConversions();
119-
120-
GenerateExplicitConversions();
117+
GenerateConversionsFromValue();
118+
GenerateConversionsToValue();
121119
GenerateEqualityOperators();
122120
GenerateEquals();
123121
GenerateGetHashCode();
@@ -129,8 +127,11 @@ private void GenerateUnion(CancellationToken cancellationToken)
129127
}");
130128
}
131129

132-
private void GenerateImplicitConversions()
130+
private void GenerateConversionsFromValue()
133131
{
132+
if (_state.Settings.ConversionFromValue == ConversionOperatorsGeneration.None)
133+
return;
134+
134135
foreach (var memberType in _state.MemberTypes)
135136
{
136137
if (memberType.IsInterface || memberType.TypeDuplicateCounter != 0)
@@ -139,19 +140,22 @@ private void GenerateImplicitConversions()
139140
_sb.Append(@"
140141
141142
/// <summary>
142-
/// Implicit conversion from type ").AppendTypeForXmlComment(memberType).Append(@".
143+
/// ").Append(_state.Settings.ConversionFromValue == ConversionOperatorsGeneration.Implicit ? "Implicit" : "Explicit").Append(" conversion from type ").AppendTypeForXmlComment(memberType).Append(@".
143144
/// </summary>
144145
/// <param name=""").Append(memberType.ArgumentName).Append(@""">Value to covert from.</param>
145146
/// <returns>A new instance of ").AppendTypeForXmlComment(_state).Append(@" converted from <paramref name=""").Append(memberType.ArgumentName).Append(@"""/>.</returns>
146-
public static implicit operator ").AppendTypeFullyQualified(_state).Append("(").AppendTypeFullyQualified(memberType).Append(" ").AppendEscaped(memberType.ArgumentName).Append(@")
147+
public static ").AppendConversionOperator(_state.Settings.ConversionFromValue).Append(" operator ").AppendTypeFullyQualified(_state).Append("(").AppendTypeFullyQualified(memberType).Append(" ").AppendEscaped(memberType.ArgumentName).Append(@")
147148
{
148149
return new ").AppendTypeFullyQualified(_state).Append("(").AppendEscaped(memberType.ArgumentName).Append(@");
149150
}");
150151
}
151152
}
152153

153-
private void GenerateExplicitConversions()
154+
private void GenerateConversionsToValue()
154155
{
156+
if (_state.Settings.ConversionToValue == ConversionOperatorsGeneration.None)
157+
return;
158+
155159
foreach (var memberType in _state.MemberTypes)
156160
{
157161
if (memberType.IsInterface || memberType.TypeDuplicateCounter != 0)
@@ -160,12 +164,12 @@ private void GenerateExplicitConversions()
160164
_sb.Append(@"
161165
162166
/// <summary>
163-
/// Implicit conversion to type ").AppendTypeForXmlComment(memberType).Append(@".
167+
/// ").Append(_state.Settings.ConversionToValue == ConversionOperatorsGeneration.Implicit ? "Implicit" : "Explicit").Append(" conversion to type ").AppendTypeForXmlComment(memberType).Append(@".
164168
/// </summary>
165169
/// <param name=""obj"">Object to covert.</param>
166170
/// <returns>Inner value of type ").AppendTypeForXmlComment(memberType).Append(@".</returns>
167171
/// <exception cref=""System.InvalidOperationException"">If the inner value is not a ").AppendTypeForXmlComment(memberType).Append(@".</exception>
168-
public static explicit operator ").AppendTypeFullyQualified(memberType).Append("(").AppendTypeFullyQualified(_state).Append(@" obj)
172+
public static ").AppendConversionOperator(_state.Settings.ConversionToValue).Append(" operator ").AppendTypeFullyQualified(memberType).Append("(").AppendTypeFullyQualified(_state).Append(@" obj)
169173
{
170174
return obj.As").Append(memberType.Name).Append(@";
171175
}");

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionSettings.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ public sealed class AdHocUnionSettings : IEquatable<AdHocUnionSettings>
1010
public IReadOnlyList<AdHocUnionMemberTypeSetting> MemberTypeSettings { get; }
1111
public StringComparison DefaultStringComparison { get; }
1212
public UnionConstructorAccessModifier ConstructorAccessModifier { get; }
13-
public bool SkipImplicitConversionFromValue { get; }
13+
public ConversionOperatorsGeneration ConversionFromValue { get; }
14+
public ConversionOperatorsGeneration ConversionToValue { get; }
1415
public bool HasStructLayoutAttribute => _attributeInfo.HasStructLayoutAttribute;
1516

1617
public AdHocUnionSettings(
@@ -23,7 +24,8 @@ public AdHocUnionSettings(
2324
MapMethods = attribute.FindMapMethods();
2425
DefaultStringComparison = attribute.FindDefaultStringComparison();
2526
ConstructorAccessModifier = attribute.FindUnionConstructorAccessModifier();
26-
SkipImplicitConversionFromValue = attribute.FindSkipImplicitConversionFromValue();
27+
ConversionFromValue = attribute.FindConversionFromValue() ?? ConversionOperatorsGeneration.Implicit;
28+
ConversionToValue = attribute.FindConversionToValue() ?? ConversionOperatorsGeneration.Explicit;
2729
_attributeInfo = attributeInfo;
2830

2931
var memberTypeSettings = new AdHocUnionMemberTypeSetting[numberOfMemberTypes];
@@ -53,7 +55,8 @@ public bool Equals(AdHocUnionSettings? other)
5355
&& MapMethods == other.MapMethods
5456
&& DefaultStringComparison == other.DefaultStringComparison
5557
&& ConstructorAccessModifier == other.ConstructorAccessModifier
56-
&& SkipImplicitConversionFromValue == other.SkipImplicitConversionFromValue
58+
&& ConversionFromValue == other.ConversionFromValue
59+
&& ConversionToValue == other.ConversionToValue
5760
&& HasStructLayoutAttribute == other.HasStructLayoutAttribute
5861
&& MemberTypeSettings.SequenceEqual(other.MemberTypeSettings);
5962
}
@@ -67,7 +70,8 @@ public override int GetHashCode()
6770
hashCode = (hashCode * 397) ^ MapMethods.GetHashCode();
6871
hashCode = (hashCode * 397) ^ (int)DefaultStringComparison;
6972
hashCode = (hashCode * 397) ^ (int)ConstructorAccessModifier;
70-
hashCode = (hashCode * 397) ^ SkipImplicitConversionFromValue.GetHashCode();
73+
hashCode = (hashCode * 397) ^ (int)ConversionFromValue;
74+
hashCode = (hashCode * 397) ^ (int)ConversionToValue;
7175
hashCode = (hashCode * 397) ^ HasStructLayoutAttribute.GetHashCode();
7276
hashCode = (hashCode * 397) ^ MemberTypeSettings.ComputeHashCode();
7377

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Unions/UnionCodeGenerator.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,17 @@ private void GenerateUnion(CancellationToken cancellationToken)
168168
GenerateMap(true);
169169
}
170170

171-
if (!_state.Settings.SkipImplicitConversionFromValue)
172-
GenerateImplicitConversions();
171+
GenerateConversionsFromValue();
173172

174173
_sb.Append(@"
175174
}");
176175
}
177176

178-
private void GenerateImplicitConversions()
177+
private void GenerateConversionsFromValue()
179178
{
179+
if (_state.Settings.ConversionFromValue == ConversionOperatorsGeneration.None)
180+
return;
181+
180182
for (var i = 0; i < _state.TypeMembers.Count; i++)
181183
{
182184
var memberType = _state.TypeMembers[i];
@@ -188,17 +190,17 @@ private void GenerateImplicitConversions()
188190
{
189191
var ctorArg = memberType.UniqueSingleArgumentConstructors[j];
190192

191-
if(ctorArg.IsInterface)
193+
if (ctorArg.IsInterface)
192194
continue;
193195

194196
_sb.Append(@"
195197
196198
/// <summary>
197-
/// Implicit conversion from type ").AppendTypeForXmlComment(ctorArg).Append(@".
199+
/// ").Append(_state.Settings.ConversionFromValue == ConversionOperatorsGeneration.Implicit ? "Implicit" : "Explicit").Append(" conversion from type ").AppendTypeForXmlComment(ctorArg).Append(@".
198200
/// </summary>
199201
/// <param name=""").Append(ctorArg.ArgumentName).Append(@""">Value to covert from.</param>
200202
/// <returns>A new instance of ").AppendTypeForXmlComment(memberType).Append(@" converted from <paramref name=""").Append(ctorArg.ArgumentName).Append(@"""/>.</returns>
201-
public static implicit operator ").AppendTypeFullyQualified(_state).Append("(").AppendTypeFullyQualified(ctorArg).Append(" ").AppendEscaped(ctorArg.ArgumentName).Append(@")
203+
public static ").AppendConversionOperator(_state.Settings.ConversionFromValue).Append(" operator ").AppendTypeFullyQualified(_state).Append("(").AppendTypeFullyQualified(ctorArg).Append(" ").AppendEscaped(ctorArg.ArgumentName).Append(@")
202204
{
203205
return new ").AppendTypeFullyQualified(memberType).Append("(").AppendEscaped(ctorArg.ArgumentName).Append(@");
204206
}");

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Unions/UnionSettings.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ public sealed class UnionSettings : IEquatable<UnionSettings>, IHashCodeComputab
44
{
55
public SwitchMapMethodsGeneration SwitchMethods { get; }
66
public SwitchMapMethodsGeneration MapMethods { get; }
7-
public bool SkipImplicitConversionFromValue { get; }
7+
public ConversionOperatorsGeneration ConversionFromValue { get; }
88

99
public UnionSettings(AttributeData attribute)
1010
{
1111
SwitchMethods = attribute.FindSwitchMethods();
1212
MapMethods = attribute.FindMapMethods();
13-
SkipImplicitConversionFromValue = attribute.FindSkipImplicitConversionFromValue();
13+
ConversionFromValue = attribute.FindConversionFromValue() ?? ConversionOperatorsGeneration.Implicit;
1414
}
1515

1616
public override bool Equals(object? obj)
@@ -28,7 +28,7 @@ public bool Equals(UnionSettings? other)
2828

2929
return SwitchMethods == other.SwitchMethods
3030
&& MapMethods == other.MapMethods
31-
&& SkipImplicitConversionFromValue == other.SkipImplicitConversionFromValue;
31+
&& ConversionFromValue == other.ConversionFromValue;
3232
}
3333

3434
public override int GetHashCode()
@@ -37,7 +37,7 @@ public override int GetHashCode()
3737
{
3838
var hashCode = SwitchMethods.GetHashCode();
3939
hashCode = (hashCode * 397) ^ MapMethods.GetHashCode();
40-
hashCode = (hashCode * 397) ^ SkipImplicitConversionFromValue.GetHashCode();
40+
hashCode = (hashCode * 397) ^ ConversionFromValue.GetHashCode();
4141

4242
return hashCode;
4343
}

src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/AttributeDataExtensions.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ public static SwitchMapMethodsGeneration FindMapMethods(this AttributeData attri
144144
return GetSwitchMapGeneration(attributeData, "MapMethods");
145145
}
146146

147+
public static ConversionOperatorsGeneration? FindConversionFromValue(this AttributeData attributeData)
148+
{
149+
return GetConversionOperatorsGeneration(attributeData, "ConversionFromValue");
150+
}
151+
152+
public static ConversionOperatorsGeneration? FindConversionToValue(this AttributeData attributeData)
153+
{
154+
return GetConversionOperatorsGeneration(attributeData, "ConversionToValue");
155+
}
156+
147157
public static ConversionOperatorsGeneration? FindConversionToKeyMemberType(this AttributeData attributeData)
148158
{
149159
return GetConversionOperatorsGeneration(attributeData, "ConversionToKeyMemberType");
@@ -205,11 +215,6 @@ public static UnionConstructorAccessModifier FindUnionConstructorAccessModifier(
205215
?? UnionConstructorAccessModifier.Public;
206216
}
207217

208-
public static bool FindSkipImplicitConversionFromValue(this AttributeData attributeData)
209-
{
210-
return GetBooleanParameterValue(attributeData, "SkipImplicitConversionFromValue") ?? false;
211-
}
212-
213218
public static bool FindAllowDefaultStructs(this AttributeData attributeData)
214219
{
215220
return GetBooleanParameterValue(attributeData, Constants.Attributes.Properties.ALLOW_DEFAULT_STRUCTS) ?? false;

src/Thinktecture.Runtime.Extensions/UnionAttribute.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace Thinktecture;
44
/// Marks a type as a discriminated union.
55
/// </summary>
66
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
7-
public class UnionAttribute : Attribute
7+
public sealed class UnionAttribute : Attribute
88
{
99
/// <summary>
1010
/// Indication whether and how the generator should generate the methods <c>Switch</c>.
@@ -17,7 +17,16 @@ public class UnionAttribute : Attribute
1717
public SwitchMapMethodsGeneration MapMethods { get; set; }
1818

1919
/// <summary>
20-
/// Indication whether the generator should skip the implementation of implicit conversions from value to union type.
20+
/// Indication whether and how the generator should generate the conversion operators from value to union type.
21+
/// Default is <see cref="ConversionOperatorsGeneration.Implicit"/>.
2122
/// </summary>
22-
public bool SkipImplicitConversionFromValue { get; set; }
23+
public ConversionOperatorsGeneration ConversionFromValue { get; set; }
24+
25+
/// <summary>
26+
/// Initializes a new instance of <see cref="UnionAttribute"/>.
27+
/// </summary>
28+
public UnionAttribute()
29+
{
30+
ConversionFromValue = ConversionOperatorsGeneration.Implicit;
31+
}
2332
}

src/Thinktecture.Runtime.Extensions/UnionAttributeBase.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,16 @@ public abstract class UnionAttributeBase : Attribute
2626
public UnionConstructorAccessModifier ConstructorAccessModifier { get; set; } = UnionConstructorAccessModifier.Public;
2727

2828
/// <summary>
29-
/// Indication whether the generator should skip the implementation of implicit conversions from value to union type.
29+
/// Indication whether and how the generator should generate the conversion operators from value to union type.
30+
/// Default is <see cref="ConversionOperatorsGeneration.Implicit"/>.
3031
/// </summary>
31-
public bool SkipImplicitConversionFromValue { get; set; }
32+
public ConversionOperatorsGeneration ConversionFromValue { get; set; }
33+
34+
/// <summary>
35+
/// Indication whether and how the generator should generate the conversion operators from union type to value.
36+
/// Default is <see cref="ConversionOperatorsGeneration.Explicit"/>.
37+
/// </summary>
38+
public ConversionOperatorsGeneration ConversionToValue { get; set; }
3239

3340
/// <summary>
3441
/// Indication whether and how the generator should generate the methods <c>Switch</c>.
@@ -39,4 +46,13 @@ public abstract class UnionAttributeBase : Attribute
3946
/// Indication whether and how the generator should generate the methods <c>Map</c>.
4047
/// </summary>
4148
public SwitchMapMethodsGeneration MapMethods { get; set; }
49+
50+
/// <summary>
51+
/// Initializes a new instance of <see cref="UnionAttributeBase"/>.
52+
/// </summary>
53+
protected UnionAttributeBase()
54+
{
55+
ConversionFromValue = ConversionOperatorsGeneration.Implicit;
56+
ConversionToValue = ConversionOperatorsGeneration.Explicit;
57+
}
4258
}

src/Thinktecture.Runtime.Extensions/UnionAttribute`2.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Thinktecture;
66
/// <typeparam name="T1">One of the types of the discriminated union.</typeparam>
77
/// <typeparam name="T2">One of the types of the discriminated union.</typeparam>
88
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
9-
public class UnionAttribute<T1, T2> : UnionAttributeBase
9+
public sealed class UnionAttribute<T1, T2> : UnionAttributeBase
1010
{
1111
/// <summary>
1212
/// Changes the name of all members regarding <typeparamref name="T1"/>.
@@ -40,7 +40,7 @@ public class UnionAttribute<T1, T2> : UnionAttributeBase
4040
/// <typeparam name="T2">One of the types of the discriminated union.</typeparam>
4141
/// <typeparam name="T3">One of the types of the discriminated union.</typeparam>
4242
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
43-
public class UnionAttribute<T1, T2, T3> : UnionAttributeBase
43+
public sealed class UnionAttribute<T1, T2, T3> : UnionAttributeBase
4444
{
4545
/// <summary>
4646
/// Changes the name of all members regarding <typeparamref name="T1"/>.
@@ -87,7 +87,7 @@ public class UnionAttribute<T1, T2, T3> : UnionAttributeBase
8787
/// <typeparam name="T3">One of the types of the discriminated union.</typeparam>
8888
/// <typeparam name="T4">One of the types of the discriminated union.</typeparam>
8989
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
90-
public class UnionAttribute<T1, T2, T3, T4> : UnionAttributeBase
90+
public sealed class UnionAttribute<T1, T2, T3, T4> : UnionAttributeBase
9191
{
9292
/// <summary>
9393
/// Changes the name of all members regarding <typeparamref name="T1"/>.
@@ -147,7 +147,7 @@ public class UnionAttribute<T1, T2, T3, T4> : UnionAttributeBase
147147
/// <typeparam name="T4">One of the types of the discriminated union.</typeparam>
148148
/// <typeparam name="T5">One of the types of the discriminated union.</typeparam>
149149
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
150-
public class UnionAttribute<T1, T2, T3, T4, T5> : UnionAttributeBase
150+
public sealed class UnionAttribute<T1, T2, T3, T4, T5> : UnionAttributeBase
151151
{
152152
/// <summary>
153153
/// Changes the name of all members regarding <typeparamref name="T1"/>.

test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/AdHocUnionSourceGeneratorTests.Should_generate_class_with_array.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ namespace Thinktecture.Tests
209209
}
210210

211211
/// <summary>
212-
/// Implicit conversion to type <c>string[]</c>.
212+
/// Explicit conversion to type <c>string[]</c>.
213213
/// </summary>
214214
/// <param name="obj">Object to covert.</param>
215215
/// <returns>Inner value of type <c>string[]</c>.</returns>
@@ -220,7 +220,7 @@ namespace Thinktecture.Tests
220220
}
221221

222222
/// <summary>
223-
/// Implicit conversion to type <see cref="int"/>.
223+
/// Explicit conversion to type <see cref="int"/>.
224224
/// </summary>
225225
/// <param name="obj">Object to covert.</param>
226226
/// <returns>Inner value of type <see cref="int"/>.</returns>

test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/AdHocUnionSourceGeneratorTests.Should_generate_class_with_generics.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ namespace Thinktecture.Tests
209209
}
210210

211211
/// <summary>
212-
/// Implicit conversion to type <c>global::System.Collections.Generic.List&lt;string&gt;</c>.
212+
/// Explicit conversion to type <c>global::System.Collections.Generic.List&lt;string&gt;</c>.
213213
/// </summary>
214214
/// <param name="obj">Object to covert.</param>
215215
/// <returns>Inner value of type <c>global::System.Collections.Generic.List&lt;string&gt;</c>.</returns>
@@ -220,7 +220,7 @@ namespace Thinktecture.Tests
220220
}
221221

222222
/// <summary>
223-
/// Implicit conversion to type <c>global::System.Collections.Generic.List&lt;int&gt;</c>.
223+
/// Explicit conversion to type <c>global::System.Collections.Generic.List&lt;int&gt;</c>.
224224
/// </summary>
225225
/// <param name="obj">Object to covert.</param>
226226
/// <returns>Inner value of type <c>global::System.Collections.Generic.List&lt;int&gt;</c>.</returns>

test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/AdHocUnionSourceGeneratorTests.Should_generate_class_with_nullable_string_and_nullable_int.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ namespace Thinktecture.Tests
209209
}
210210

211211
/// <summary>
212-
/// Implicit conversion to type <c>string?</c>.
212+
/// Explicit conversion to type <c>string?</c>.
213213
/// </summary>
214214
/// <param name="obj">Object to covert.</param>
215215
/// <returns>Inner value of type <c>string?</c>.</returns>
@@ -220,7 +220,7 @@ namespace Thinktecture.Tests
220220
}
221221

222222
/// <summary>
223-
/// Implicit conversion to type <c>int?</c>.
223+
/// Explicit conversion to type <c>int?</c>.
224224
/// </summary>
225225
/// <param name="obj">Object to covert.</param>
226226
/// <returns>Inner value of type <c>int?</c>.</returns>

test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/AdHocUnionSourceGeneratorTests.Should_generate_class_with_same_member_types.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ namespace Thinktecture.Tests
345345
}
346346

347347
/// <summary>
348-
/// Implicit conversion to type <see cref="int"/>.
348+
/// Explicit conversion to type <see cref="int"/>.
349349
/// </summary>
350350
/// <param name="obj">Object to covert.</param>
351351
/// <returns>Inner value of type <see cref="int"/>.</returns>
@@ -356,7 +356,7 @@ namespace Thinktecture.Tests
356356
}
357357

358358
/// <summary>
359-
/// Implicit conversion to type <c>int?</c>.
359+
/// Explicit conversion to type <c>int?</c>.
360360
/// </summary>
361361
/// <param name="obj">Object to covert.</param>
362362
/// <returns>Inner value of type <c>int?</c>.</returns>

0 commit comments

Comments
 (0)