Skip to content

fix: preserve nullable annotation on generic return type in setup interface#723

Merged
vbreuss merged 1 commit into
mainfrom
topic/preserve-nullable-annotation-on-generic-return-type
Apr 30, 2026
Merged

fix: preserve nullable annotation on generic return type in setup interface#723
vbreuss merged 1 commit into
mainfrom
topic/preserve-nullable-annotation-on-generic-return-type

Conversation

@vbreuss
Copy link
Copy Markdown
Member

@vbreuss vbreuss commented Apr 30, 2026

A T? return on a generic method whose T carries a non-value-type constraint (class, class?, an interface, or notnull) cannot be expressed in the explicit setup-interface implementation: CS0460 forbids restating the inherited constraint and where T : default (CS8822) conflicts with those constraints. Without a clause the compiler resolves bare T? as Nullable<T> and reports CS0453/CS9334/CS0738/CS0266.

The setup-side return type now drops the trailing ? for these methods. NRT annotations are erased at runtime, so the underlying setup object is unchanged. The user-facing mock body keeps T? because the constraint is visible there.

…erface

A `T?` return on a generic method whose `T` carries a non-value-type constraint
(`class`, `class?`, an interface, or `notnull`) cannot be expressed in the explicit
setup-interface implementation: CS0460 forbids restating the inherited constraint
and `where T : default` (CS8822) conflicts with those constraints. Without a clause
the compiler resolves bare `T?` as `Nullable<T>` and reports CS0453/CS9334/CS0738/CS0266.

The setup-side return type now drops the trailing `?` for these methods. NRT
annotations are erased at runtime, so the underlying setup object is unchanged.
The user-facing mock body keeps `T?` because the constraint is visible there.
@vbreuss vbreuss self-assigned this Apr 30, 2026
@vbreuss vbreuss added the bug Something isn't working label Apr 30, 2026
@vbreuss vbreuss enabled auto-merge (squash) April 30, 2026 05:12
@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown

Test Results

    21 files  ±0      21 suites  ±0   9m 34s ⏱️ - 1m 46s
 3 850 tests ±0   3 849 ✅ ±0  1 💤 ±0  0 ❌ ±0 
25 064 runs  ±0  25 063 ✅ ±0  1 💤 ±0  0 ❌ ±0 

Results for commit 8eedb33. ± Comparison against base commit 16693da.

@vbreuss vbreuss merged commit 74753ac into main Apr 30, 2026
16 checks passed
@vbreuss vbreuss deleted the topic/preserve-nullable-annotation-on-generic-return-type branch April 30, 2026 05:21
@github-actions
Copy link
Copy Markdown

🚀 Benchmark Results

Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.79GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.203
[Host] : .NET 10.0.7 (10.0.7, 10.0.726.21808), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Event_Mockolate 338.5 ns 6.26 ns 5.86 ns 1.00 0.02 0.1254 0.0005 2.05 KB 1.00
Event_Moq 15,977.0 ns 49.92 ns 44.25 ns 47.21 0.79 0.7324 - 12.51 KB 6.09
Event_NSubstitute 5,713.4 ns 42.78 ns 40.02 ns 16.88 0.30 0.5493 0.0076 9.05 KB 4.40
Event_FakeItEasy 212,533.4 ns 940.83 ns 834.02 ns 628.05 10.70 0.7324 0.4883 15.39 KB 7.49
Event_Imposter 1,390.5 ns 15.86 ns 14.84 ns 4.11 0.08 0.5379 0.0153 8.8 KB 4.29
Event_TUnitMocks 184.0 ns 1.58 ns 1.48 ns 0.54 0.01 0.0837 0.0002 1.37 KB 0.67
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.203
[Host] : .NET 10.0.7 (10.0.7, 10.0.726.21808), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method N Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Indexer_Mockolate 1 1,054.5 ns 47.21 ns 44.16 ns 1.00 0.06 0.2460 0.0019 4.03 KB 1.00
Indexer_Moq 1 218,590.8 ns 1,221.96 ns 1,083.24 ns 207.63 8.59 0.9766 0.4883 20.57 KB 5.10
Indexer_NSubstitute 1 9,733.9 ns 39.24 ns 36.71 ns 9.25 0.38 0.7782 - 12.84 KB 3.18
Indexer_FakeItEasy 1 12,587.9 ns 62.64 ns 55.53 ns 11.96 0.49 0.8392 - 13.96 KB 3.46
Indexer_Imposter 1 998.0 ns 45.65 ns 42.70 ns 0.95 0.06 0.3147 0.0038 5.16 KB 1.28
Indexer_Mockolate 10 2,629.0 ns 12.46 ns 11.65 ns 1.00 0.01 0.3090 - 5.09 KB 1.00
Indexer_Moq 10 230,324.5 ns 1,042.07 ns 974.76 ns 87.61 0.52 1.7090 1.4648 30.09 KB 5.92
Indexer_NSubstitute 10 23,416.4 ns 68.94 ns 61.11 ns 8.91 0.04 1.5564 0.0305 25.63 KB 5.04
Indexer_FakeItEasy 10 26,932.2 ns 308.52 ns 288.59 ns 10.24 0.12 2.1667 - 35.55 KB 6.99
Indexer_Imposter 10 2,318.2 ns 20.98 ns 19.62 ns 0.88 0.01 0.4845 0.0076 7.97 KB 1.57
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.203
[Host] : .NET 10.0.7 (10.0.7, 10.0.726.21808), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method N Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Method_Mockolate 1 485.8 ns 23.54 ns 20.87 ns 1.00 0.06 0.1416 0.0010 2.32 KB 1.00
Method_Moq 1 180,596.3 ns 868.20 ns 769.64 ns 372.37 15.43 0.4883 - 14.58 KB 6.28
Method_NSubstitute 1 5,716.5 ns 75.07 ns 70.22 ns 11.79 0.51 0.5569 0.0076 9.12 KB 3.93
Method_FakeItEasy 1 5,965.7 ns 62.09 ns 58.08 ns 12.30 0.52 0.4959 - 8.11 KB 3.50
Method_Imposter 1 602.7 ns 25.58 ns 23.93 ns 1.24 0.07 0.2470 0.0029 4.04 KB 1.74
Method_TUnitMocks 1 679.2 ns 14.95 ns 13.25 ns 1.40 0.06 0.1774 0.0010 2.9 KB 1.25
Method_Mockolate 10 758.4 ns 8.70 ns 8.14 ns 1.00 0.01 0.1545 0.0010 2.53 KB 1.00
Method_Moq 10 186,650.2 ns 1,007.00 ns 892.68 ns 246.14 2.78 0.9766 0.7324 18.48 KB 7.30
Method_NSubstitute 10 8,601.0 ns 105.32 ns 98.51 ns 11.34 0.17 0.7324 0.0153 12.07 KB 4.77
Method_FakeItEasy 10 9,585.8 ns 89.75 ns 79.56 ns 12.64 0.17 0.9766 0.0305 16.05 KB 6.34
Method_Imposter 10 1,216.8 ns 31.45 ns 29.41 ns 1.60 0.04 0.3376 0.0057 5.52 KB 2.18
Method_TUnitMocks 10 1,759.1 ns 32.63 ns 30.53 ns 2.32 0.05 0.2747 0.0019 4.49 KB 1.77
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.203
[Host] : .NET 10.0.7 (10.0.7, 10.0.726.21808), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method N Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Property_Mockolate 1 517.3 ns 3.08 ns 2.73 ns 1.00 0.01 0.1574 0.0010 2.58 KB 1.00
Property_Moq 1 11,667.5 ns 81.47 ns 72.22 ns 22.56 0.18 0.6104 - 10.39 KB 4.03
Property_NSubstitute 1 7,394.8 ns 16.49 ns 14.62 ns 14.30 0.08 0.6943 0.0076 11.45 KB 4.44
Property_FakeItEasy 1 8,220.2 ns 16.81 ns 14.90 ns 15.89 0.09 0.6866 - 11.24 KB 4.36
Property_Imposter 1 430.9 ns 1.01 ns 0.84 ns 0.83 0.00 0.1912 0.0019 3.13 KB 1.21
Property_TUnitMocks 1 738.6 ns 1.18 ns 1.10 ns 1.43 0.01 0.1526 0.0010 2.51 KB 0.97
Property_Mockolate 10 1,030.1 ns 3.28 ns 3.07 ns 1.00 0.00 0.1869 - 3.07 KB 1.00
Property_Moq 10 18,267.0 ns 41.67 ns 38.98 ns 17.73 0.06 1.0986 - 18.28 KB 5.95
Property_NSubstitute 10 17,134.8 ns 22.57 ns 20.01 ns 16.63 0.05 1.2817 0.0305 21.08 KB 6.87
Property_FakeItEasy 10 19,496.7 ns 43.41 ns 40.61 ns 18.93 0.07 1.8616 - 30.81 KB 10.03
Property_Imposter 10 1,101.5 ns 1.97 ns 1.65 ns 1.07 0.00 0.2842 0.0019 4.67 KB 1.52
Property_TUnitMocks 10 2,115.1 ns 5.24 ns 4.64 ns 2.05 0.01 0.2823 - 4.66 KB 1.52
Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.79GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.203
[Host] : .NET 10.0.7 (10.0.7, 10.0.726.21808), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Callback_Mockolate 397.4 ns 5.10 ns 4.77 ns 1.00 0.02 0.1197 - 1.96 KB 1.00
Callback_Moq 96,915.8 ns 356.44 ns 315.97 ns 243.92 2.92 0.4883 0.2441 8.88 KB 4.53
Callback_NSubstitute 4,218.9 ns 19.21 ns 17.97 ns 10.62 0.13 0.4730 0.0076 7.74 KB 3.95
Callback_FakeItEasy 4,658.8 ns 11.46 ns 8.95 ns 11.73 0.14 0.4120 0.0153 6.81 KB 3.47
Callback_Imposter 396.8 ns 1.80 ns 1.51 ns 1.00 0.01 0.1454 0.0010 2.38 KB 1.22
Callback_TUnitMocks 587.0 ns 3.00 ns 2.50 ns 1.48 0.02 0.1602 0.0010 2.63 KB 1.34

@github-actions github-actions Bot added the state: released The issue is released label Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working state: released The issue is released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant