Skip to content

Commit a12d6ab

Browse files
Alternative approach
To prevent calling overloads using implicit operators we need a generic fall back. As we can only have 1 generic method we have to write out the Numeric overload into its 11 separate types.
1 parent 36b8b1d commit a12d6ab

File tree

6 files changed

+203
-91
lines changed

6 files changed

+203
-91
lines changed

src/NUnitFramework/framework/Constraints/ConstraintExpression.cs

+89-6
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ public Constraint Matches<TActual>(Predicate<TActual> predicate)
422422
/// <summary>
423423
/// Returns a constraint that tests two items for equality
424424
/// </summary>
425-
public EqualConstraint EqualTo(object? expected)
425+
public EqualConstraint EqualTo<T>(T? expected)
426426
{
427427
return Append(new EqualConstraint(expected));
428428
}
@@ -462,13 +462,96 @@ public EqualTimeBaseConstraint<TimeSpan> EqualTo(TimeSpan expected)
462462
/// <summary>
463463
/// Returns a constraint that tests two numbers for equality
464464
/// </summary>
465-
#pragma warning disable CS3024 // Constraint type is not CLS-compliant
466-
public EqualNumericConstraint<T> EqualTo<T>(T expected)
467-
where T : unmanaged, IConvertible, IEquatable<T>
465+
public EqualNumericConstraint<double> EqualTo(double expected)
468466
{
469-
return Append(new EqualNumericConstraint<T>(expected));
467+
return Append(new EqualNumericConstraint<double>(expected));
470468
}
471-
#pragma warning restore CS3024 // Constraint type is not CLS-compliant
469+
470+
/// <summary>
471+
/// Returns a constraint that tests two numbers for equality
472+
/// </summary>
473+
public EqualNumericConstraint<float> EqualTo(float expected)
474+
{
475+
return Append(new EqualNumericConstraint<float>(expected));
476+
}
477+
478+
/// <summary>
479+
/// Returns a constraint that tests two numbers for equality
480+
/// </summary>
481+
public EqualNumericConstraint<decimal> EqualTo(decimal expected)
482+
{
483+
return Append(new EqualNumericConstraint<decimal>(expected));
484+
}
485+
486+
/// <summary>
487+
/// Returns a constraint that tests two numbers for equality
488+
/// </summary>
489+
public EqualNumericConstraint<long> EqualTo(long expected)
490+
{
491+
return Append(new EqualNumericConstraint<long>(expected));
492+
}
493+
494+
/// <summary>
495+
/// Returns a constraint that tests two numbers for equality
496+
/// </summary>
497+
public EqualNumericConstraint<int> EqualTo(int expected)
498+
{
499+
return Append(new EqualNumericConstraint<int>(expected));
500+
}
501+
502+
/// <summary>
503+
/// Returns a constraint that tests two numbers for equality
504+
/// </summary>
505+
public EqualNumericConstraint<short> EqualTo(short expected)
506+
{
507+
return Append(new EqualNumericConstraint<short>(expected));
508+
}
509+
510+
/// <summary>
511+
/// Returns a constraint that tests two numbers for equality
512+
/// </summary>
513+
public EqualNumericConstraint<byte> EqualTo(byte expected)
514+
{
515+
return Append(new EqualNumericConstraint<byte>(expected));
516+
}
517+
518+
#pragma warning disable CS3002 // Return type is not CLS-compliant
519+
#pragma warning disable CS3001 // Argument type is not CLS-compliant
520+
521+
/// <summary>
522+
/// Returns a constraint that tests two numbers for equality
523+
/// </summary>
524+
public EqualNumericConstraint<ulong> EqualTo(ulong expected)
525+
{
526+
return Append(new EqualNumericConstraint<ulong>(expected));
527+
}
528+
529+
/// <summary>
530+
/// Returns a constraint that tests two numbers for equality
531+
/// </summary>
532+
public EqualNumericConstraint<uint> EqualTo(uint expected)
533+
{
534+
return Append(new EqualNumericConstraint<uint>(expected));
535+
}
536+
537+
/// <summary>
538+
/// Returns a constraint that tests two numbers for equality
539+
/// </summary>
540+
public EqualNumericConstraint<ushort> EqualTo(ushort expected)
541+
{
542+
return Append(new EqualNumericConstraint<ushort>(expected));
543+
}
544+
545+
/// <summary>
546+
/// Returns a constraint that tests two numbers for equality
547+
/// </summary>
548+
public EqualNumericConstraint<sbyte> EqualTo(sbyte expected)
549+
{
550+
return Append(new EqualNumericConstraint<sbyte>(expected));
551+
}
552+
553+
#pragma warning restore CS3001 // Argument type is not CLS-compliant
554+
#pragma warning restore CS3002 // Return type is not CLS-compliant
472555

473556
#endregion
474557

src/NUnitFramework/framework/Constraints/EqualNumericConstraint.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ public class EqualNumericConstraint<T> : EqualNumericWithoutUsingConstraint<T>,
2020
/// <summary>
2121
/// Initializes a new instance of the <see cref="EqualConstraint"/> class.
2222
/// </summary>
23+
/// <remarks>
24+
/// Marked internal to prevent external instantiation with non-supported types.
25+
/// </remarks>
2326
/// <param name="expected">The expected value.</param>
24-
public EqualNumericConstraint(T expected)
27+
internal EqualNumericConstraint(T expected)
2528
: base(expected)
2629
{
2730
}

src/NUnitFramework/framework/Constraints/EqualNumericWithoutUsingConstraint.cs

+6-17
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@ public class EqualNumericWithoutUsingConstraint<T> : Constraint
2929
/// <summary>
3030
/// Initializes a new instance of the <see cref="EqualConstraint"/> class.
3131
/// </summary>
32+
/// <remarks>
33+
/// Marked internal to prevent external instantiation with non-supported types.
34+
/// </remarks>
3235
/// <param name="expected">The expected value.</param>
33-
public EqualNumericWithoutUsingConstraint(T expected)
36+
internal EqualNumericWithoutUsingConstraint(T expected)
3437
: base(expected)
3538
{
3639
_expected = expected;
@@ -123,21 +126,7 @@ public EqualNumericWithoutUsingConstraint<T> Percent
123126
/// <returns>True for success, false for failure</returns>
124127
public ConstraintResult ApplyTo(T actual)
125128
{
126-
// As we cannot use exact generic constraints, T could be a type not supported by Numerics.
127-
// In that case fall back to the default equality comparison.
128-
bool hasSucceeded;
129-
130-
if (Numerics.IsNumericType(typeof(T)))
131-
{
132-
hasSucceeded = Numerics.AreEqual(_expected, actual, ref _tolerance);
133-
}
134-
else
135-
{
136-
if (!_tolerance.IsUnsetOrDefault)
137-
throw new InvalidOperationException("Cannot use Tolerance with IEquatable<>.");
138-
139-
hasSucceeded = _expected.Equals(actual);
140-
}
129+
bool hasSucceeded = Numerics.AreEqual(_expected, actual, ref _tolerance);
141130

142131
return ConstraintResult(actual, hasSucceeded);
143132
}
@@ -155,7 +144,7 @@ public override ConstraintResult ApplyTo<TActual>(TActual actual)
155144
{
156145
hasSucceeded = false;
157146
}
158-
else if (actual is T t && Numerics.IsNumericType(typeof(T)))
147+
else if (actual is T t)
159148
{
160149
hasSucceeded = Numerics.AreEqual(_expected, t, ref _tolerance);
161150
}

src/NUnitFramework/framework/Constraints/EqualStringWithoutUsingConstraint.cs

-25
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
22

33
using System;
4-
using System.Linq;
54
using System.Text;
65
using NUnit.Framework.Constraints.Comparers;
76

@@ -129,10 +128,6 @@ public sealed override ConstraintResult ApplyTo<TActual>(TActual actual)
129128
{
130129
return ApplyTo(actualString);
131130
}
132-
else if (CanCastToString(actual, out string? actualCastToString))
133-
{
134-
return ApplyTo(actualCastToString);
135-
}
136131
else if (actual is IEquatable<string> equatableString)
137132
{
138133
if (_caseInsensitive || _ignoringWhiteSpace)
@@ -157,26 +152,6 @@ public sealed override ConstraintResult ApplyTo<TActual>(TActual actual)
157152
return ConstraintResult(actual, hasSucceeded);
158153
}
159154

160-
private static bool CanCastToString<TActual>(TActual actual, out string? s)
161-
{
162-
// Check if the type implements an implicit cast to string
163-
// Note that we don't have to check the parameter types
164-
// as the compiler only allows cast from/to TActual and we check the return type.
165-
var implicitCastToString = typeof(TActual).GetMethods()
166-
.FirstOrDefault(x => x.IsStatic &&
167-
x.Name == "op_Implicit" &&
168-
x.ReturnType == typeof(string));
169-
170-
if (implicitCastToString is null)
171-
{
172-
s = null;
173-
return false;
174-
}
175-
176-
s = (string?)implicitCastToString.Invoke(null, [actual]);
177-
return true;
178-
}
179-
180155
private ConstraintResult ConstraintResult<T>(T actual, bool hasSucceeded)
181156
{
182157
return new EqualConstraintResult(this, actual, _caseInsensitive, _ignoringWhiteSpace, _clipStrings, hasSucceeded);

src/NUnitFramework/framework/Is.cs

+89-6
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public abstract class Is
145145
/// <summary>
146146
/// Returns a constraint that tests two items for equality
147147
/// </summary>
148-
public static EqualConstraint EqualTo(object? expected)
148+
public static EqualConstraint EqualTo<T>(T? expected)
149149
{
150150
return new EqualConstraint(expected);
151151
}
@@ -193,13 +193,96 @@ public static EqualTimeBaseConstraint<TimeSpan> EqualTo(TimeSpan expected)
193193
/// <summary>
194194
/// Returns a constraint that tests two numbers for equality
195195
/// </summary>
196-
#pragma warning disable CS3024 // Constraint type is not CLS-compliant
197-
public static EqualNumericConstraint<T> EqualTo<T>(T expected)
198-
where T : unmanaged, IConvertible, IEquatable<T>
196+
public static EqualNumericConstraint<double> EqualTo(double expected)
199197
{
200-
return new EqualNumericConstraint<T>(expected);
198+
return new EqualNumericConstraint<double>(expected);
201199
}
202-
#pragma warning restore CS3024 // Constraint type is not CLS-compliant
200+
201+
/// <summary>
202+
/// Returns a constraint that tests two numbers for equality
203+
/// </summary>
204+
public static EqualNumericConstraint<float> EqualTo(float expected)
205+
{
206+
return new EqualNumericConstraint<float>(expected);
207+
}
208+
209+
/// <summary>
210+
/// Returns a constraint that tests two numbers for equality
211+
/// </summary>
212+
public static EqualNumericConstraint<decimal> EqualTo(decimal expected)
213+
{
214+
return new EqualNumericConstraint<decimal>(expected);
215+
}
216+
217+
/// <summary>
218+
/// Returns a constraint that tests two numbers for equality
219+
/// </summary>
220+
public static EqualNumericConstraint<long> EqualTo(long expected)
221+
{
222+
return new EqualNumericConstraint<long>(expected);
223+
}
224+
225+
/// <summary>
226+
/// Returns a constraint that tests two numbers for equality
227+
/// </summary>
228+
public static EqualNumericConstraint<int> EqualTo(int expected)
229+
{
230+
return new EqualNumericConstraint<int>(expected);
231+
}
232+
233+
/// <summary>
234+
/// Returns a constraint that tests two numbers for equality
235+
/// </summary>
236+
public static EqualNumericConstraint<short> EqualTo(short expected)
237+
{
238+
return new EqualNumericConstraint<short>(expected);
239+
}
240+
241+
/// <summary>
242+
/// Returns a constraint that tests two numbers for equality
243+
/// </summary>
244+
public static EqualNumericConstraint<byte> EqualTo(byte expected)
245+
{
246+
return new EqualNumericConstraint<byte>(expected);
247+
}
248+
249+
#pragma warning disable CS3002 // Return type is not CLS-compliant
250+
#pragma warning disable CS3001 // Argument type is not CLS-compliant
251+
252+
/// <summary>
253+
/// Returns a constraint that tests two numbers for equality
254+
/// </summary>
255+
public static EqualNumericConstraint<ulong> EqualTo(ulong expected)
256+
{
257+
return new EqualNumericConstraint<ulong>(expected);
258+
}
259+
260+
/// <summary>
261+
/// Returns a constraint that tests two numbers for equality
262+
/// </summary>
263+
public static EqualNumericConstraint<uint> EqualTo(uint expected)
264+
{
265+
return new EqualNumericConstraint<uint>(expected);
266+
}
267+
268+
/// <summary>
269+
/// Returns a constraint that tests two numbers for equality
270+
/// </summary>
271+
public static EqualNumericConstraint<ushort> EqualTo(ushort expected)
272+
{
273+
return new EqualNumericConstraint<ushort>(expected);
274+
}
275+
276+
/// <summary>
277+
/// Returns a constraint that tests two numbers for equality
278+
/// </summary>
279+
public static EqualNumericConstraint<sbyte> EqualTo(sbyte expected)
280+
{
281+
return new EqualNumericConstraint<sbyte>(expected);
282+
}
283+
284+
#pragma warning restore CS3001 // Argument type is not CLS-compliant
285+
#pragma warning restore CS3002 // Return type is not CLS-compliant
203286

204287
#endregion
205288

0 commit comments

Comments
 (0)