@@ -78,6 +78,7 @@ public void FindTryParseStringMethod_ReturnsTheExpectedTryParseMethodWithInvaria
78
78
[ InlineData ( typeof ( TryParseStringRecord ) ) ]
79
79
[ InlineData ( typeof ( TryParseStringStruct ) ) ]
80
80
[ InlineData ( typeof ( TryParseInheritClassWithFormatProvider ) ) ]
81
+ [ InlineData ( typeof ( TryParseFromInterfaceWithFormatProvider ) ) ]
81
82
public void FindTryParseStringMethod_ReturnsTheExpectedTryParseMethodWithInvariantCultureCustomType ( Type type )
82
83
{
83
84
var methodFound = new ParameterBindingMethodCache ( ) . FindTryParseMethod ( @type ) ;
@@ -99,6 +100,10 @@ public void FindTryParseStringMethod_ReturnsTheExpectedTryParseMethodWithInvaria
99
100
[ InlineData ( typeof ( TryParseNoFormatProviderRecord ) ) ]
100
101
[ InlineData ( typeof ( TryParseNoFormatProviderStruct ) ) ]
101
102
[ InlineData ( typeof ( TryParseInheritClass ) ) ]
103
+ [ InlineData ( typeof ( TryParseFromInterface ) ) ]
104
+ [ InlineData ( typeof ( TryParseFromGrandparentInterface ) ) ]
105
+ [ InlineData ( typeof ( TryParseDirectlyAndFromInterface ) ) ]
106
+ [ InlineData ( typeof ( TryParseFromClassAndInterface ) ) ]
102
107
public void FindTryParseMethod_WithNoFormatProvider ( Type type )
103
108
{
104
109
var methodFound = new ParameterBindingMethodCache ( ) . FindTryParseMethod ( @type ) ;
@@ -276,7 +281,27 @@ public static IEnumerable<object[]> BindAsyncParameterInfoData
276
281
new [ ]
277
282
{
278
283
GetFirstParameter ( ( InheritBindAsyncWithParameterInfo arg ) => InheritBindAsyncWithParameterInfoMethod ( arg ) )
279
- }
284
+ } ,
285
+ new [ ]
286
+ {
287
+ GetFirstParameter ( ( BindAsyncFromInterface arg ) => BindAsyncFromInterfaceMethod ( arg ) )
288
+ } ,
289
+ new [ ]
290
+ {
291
+ GetFirstParameter ( ( BindAsyncFromGrandparentInterface arg ) => BindAsyncFromGrandparentInterfaceMethod ( arg ) )
292
+ } ,
293
+ new [ ]
294
+ {
295
+ GetFirstParameter ( ( BindAsyncDirectlyAndFromInterface arg ) => BindAsyncDirectlyAndFromInterfaceMethod ( arg ) )
296
+ } ,
297
+ new [ ]
298
+ {
299
+ GetFirstParameter ( ( BindAsyncFromClassAndInterface arg ) => BindAsyncFromClassAndInterfaceMethod ( arg ) )
300
+ } ,
301
+ new [ ]
302
+ {
303
+ GetFirstParameter ( ( BindAsyncFromInterfaceWithParameterInfo arg ) => BindAsyncFromInterfaceWithParameterInfoMethod ( arg ) )
304
+ } ,
280
305
} ;
281
306
}
282
307
}
@@ -313,6 +338,7 @@ public void FindBindAsyncMethod_FindsNonNullableReturningBindAsyncMethodGivenNul
313
338
[ InlineData ( typeof ( InvalidNonStaticTryParseStruct ) ) ]
314
339
[ InlineData ( typeof ( InvalidNonStaticTryParseClass ) ) ]
315
340
[ InlineData ( typeof ( TryParseWrongTypeInheritClass ) ) ]
341
+ [ InlineData ( typeof ( TryParseWrongTypeFromInterface ) ) ]
316
342
public void FindTryParseMethod_ThrowsIfInvalidTryParseOnType ( Type type )
317
343
{
318
344
var ex = Assert . Throws < InvalidOperationException > (
@@ -322,6 +348,14 @@ public void FindTryParseMethod_ThrowsIfInvalidTryParseOnType(Type type)
322
348
Assert . Contains ( $ "bool TryParse(string, out { TypeNameHelper . GetTypeDisplayName ( type , fullName : false ) } )", ex . Message ) ;
323
349
}
324
350
351
+ [ Fact ]
352
+ public void FindTryParseMethod_ThrowsIfMultipleInterfacesMatch ( )
353
+ {
354
+ var ex = Assert . Throws < InvalidOperationException > (
355
+ ( ) => new ParameterBindingMethodCache ( ) . FindTryParseMethod ( typeof ( TryParseFromMultipleInterfaces ) ) ) ;
356
+ Assert . Equal ( "TryParseFromMultipleInterfaces implements multiple interfaces defining a static Boolean TryParse(System.String, TryParseFromMultipleInterfaces ByRef) method causing ambiguity." , ex . Message ) ;
357
+ }
358
+
325
359
[ Theory ]
326
360
[ InlineData ( typeof ( TryParseClassWithGoodAndBad ) ) ]
327
361
[ InlineData ( typeof ( TryParseStructWithGoodAndBad ) ) ]
@@ -338,6 +372,7 @@ public void FindTryParseMethod_IgnoresInvalidTryParseIfGoodOneFound(Type type)
338
372
[ InlineData ( typeof ( InvalidWrongParamBindAsyncClass ) ) ]
339
373
[ InlineData ( typeof ( BindAsyncWrongTypeInherit ) ) ]
340
374
[ InlineData ( typeof ( BindAsyncWithParameterInfoWrongTypeInherit ) ) ]
375
+ [ InlineData ( typeof ( BindAsyncWrongTypeFromInterface ) ) ]
341
376
public void FindBindAsyncMethod_ThrowsIfInvalidBindAsyncOnType ( Type type )
342
377
{
343
378
var cache = new ParameterBindingMethodCache ( ) ;
@@ -351,6 +386,15 @@ public void FindBindAsyncMethod_ThrowsIfInvalidBindAsyncOnType(Type type)
351
386
Assert . Contains ( $ "ValueTask<{ TypeNameHelper . GetTypeDisplayName ( type , fullName : false ) } ?> BindAsync(HttpContext context)", ex . Message ) ;
352
387
}
353
388
389
+ [ Fact ]
390
+ public void FindBindAsyncMethod_ThrowsIfMultipleInterfacesMatch ( )
391
+ {
392
+ var cache = new ParameterBindingMethodCache ( ) ;
393
+ var parameter = new MockParameterInfo ( typeof ( BindAsyncFromMultipleInterfaces ) , "anything" ) ;
394
+ var ex = Assert . Throws < InvalidOperationException > ( ( ) => cache . FindBindAsyncMethod ( parameter ) ) ;
395
+ Assert . Equal ( "BindAsyncFromMultipleInterfaces implements multiple interfaces defining a static System.Threading.Tasks.ValueTask`1[Microsoft.AspNetCore.Http.Extensions.Tests.ParameterBindingMethodCacheTests+BindAsyncFromMultipleInterfaces] BindAsync(Microsoft.AspNetCore.Http.HttpContext) method causing ambiguity." , ex . Message ) ;
396
+ }
397
+
354
398
[ Theory ]
355
399
[ InlineData ( typeof ( BindAsyncStructWithGoodAndBad ) ) ]
356
400
[ InlineData ( typeof ( BindAsyncClassWithGoodAndBad ) ) ]
@@ -382,6 +426,11 @@ private static void BindAsyncSingleArgRecordMethod(BindAsyncSingleArgRecord arg)
382
426
private static void BindAsyncSingleArgStructMethod ( BindAsyncSingleArgStruct arg ) { }
383
427
private static void InheritBindAsyncMethod ( InheritBindAsync arg ) { }
384
428
private static void InheritBindAsyncWithParameterInfoMethod ( InheritBindAsyncWithParameterInfo args ) { }
429
+ private static void BindAsyncFromInterfaceMethod ( BindAsyncFromInterface arg ) { }
430
+ private static void BindAsyncFromGrandparentInterfaceMethod ( BindAsyncFromGrandparentInterface arg ) { }
431
+ private static void BindAsyncDirectlyAndFromInterfaceMethod ( BindAsyncDirectlyAndFromInterface arg ) { }
432
+ private static void BindAsyncFromClassAndInterfaceMethod ( BindAsyncFromClassAndInterface arg ) { }
433
+ private static void BindAsyncFromInterfaceWithParameterInfoMethod ( BindAsyncFromInterfaceWithParameterInfo args ) { }
385
434
386
435
private static ParameterInfo GetFirstParameter < T > ( Expression < Action < T > > expr )
387
436
{
@@ -631,6 +680,75 @@ private class TryParseInheritClassWithFormatProvider : BaseTryParseClassWithForm
631
680
{
632
681
}
633
682
683
+ private interface ITryParse < T >
684
+ {
685
+ static bool TryParse ( string ? value , out T ? result )
686
+ {
687
+ result = default ( T ) ;
688
+ return false ;
689
+ }
690
+ }
691
+
692
+ private interface ITryParse2 < T >
693
+ {
694
+ static bool TryParse ( string ? value , out T ? result )
695
+ {
696
+ result = default ( T ) ;
697
+ return false ;
698
+ }
699
+ }
700
+
701
+ private interface IImplementITryParse < T > : ITryParse < T >
702
+ {
703
+ }
704
+
705
+ private class TryParseFromInterface : ITryParse < TryParseFromInterface >
706
+ {
707
+ }
708
+
709
+ private class TryParseFromGrandparentInterface : IImplementITryParse < TryParseFromGrandparentInterface >
710
+ {
711
+ }
712
+
713
+ private class TryParseDirectlyAndFromInterface : ITryParse < TryParseDirectlyAndFromInterface >
714
+ {
715
+ static bool TryParse ( string ? value , out TryParseDirectlyAndFromInterface ? result )
716
+ {
717
+ result = null ;
718
+ return false ;
719
+ }
720
+ }
721
+
722
+ private class TryParseFromClassAndInterface
723
+ : BaseTryParseClass < TryParseFromClassAndInterface > ,
724
+ ITryParse < TryParseFromClassAndInterface >
725
+ {
726
+ }
727
+
728
+ private class TryParseFromMultipleInterfaces
729
+ : ITryParse < TryParseFromMultipleInterfaces > ,
730
+ ITryParse2 < TryParseFromMultipleInterfaces >
731
+ {
732
+ }
733
+
734
+ // using wrong T on purpose
735
+ private class TryParseWrongTypeFromInterface : ITryParse < TryParseFromInterface >
736
+ {
737
+ }
738
+
739
+ private interface ITryParseWithFormatProvider < T >
740
+ {
741
+ public static bool TryParse ( string ? value , IFormatProvider formatProvider , out T ? result )
742
+ {
743
+ result = default ( T ) ;
744
+ return false ;
745
+ }
746
+ }
747
+
748
+ private class TryParseFromInterfaceWithFormatProvider : ITryParseWithFormatProvider < TryParseFromInterfaceWithFormatProvider >
749
+ {
750
+ }
751
+
634
752
private record BindAsyncRecord ( int Value )
635
753
{
636
754
public static ValueTask < BindAsyncRecord ? > BindAsync ( HttpContext context , ParameterInfo parameter )
@@ -771,6 +889,71 @@ private class BindAsyncWithParameterInfoWrongTypeInherit : BaseBindAsyncWithPara
771
889
{
772
890
}
773
891
892
+ private interface IBindAsync < T >
893
+ {
894
+ static ValueTask < T ? > BindAsync ( HttpContext context )
895
+ {
896
+ return new ( default ( T ) ) ;
897
+ }
898
+ }
899
+
900
+ private interface IBindAsync2 < T >
901
+ {
902
+ static ValueTask < T ? > BindAsync ( HttpContext context )
903
+ {
904
+ return new ( default ( T ) ) ;
905
+ }
906
+ }
907
+
908
+ private interface IImeplmentIBindAsync < T > : IBindAsync < T >
909
+ {
910
+ }
911
+
912
+ private class BindAsyncFromInterface : IBindAsync < BindAsyncFromInterface >
913
+ {
914
+ }
915
+
916
+ private class BindAsyncFromGrandparentInterface : IImeplmentIBindAsync < BindAsyncFromGrandparentInterface >
917
+ {
918
+ }
919
+
920
+ private class BindAsyncDirectlyAndFromInterface : IBindAsync < BindAsyncDirectlyAndFromInterface >
921
+ {
922
+ static ValueTask < BindAsyncFromInterface ? > BindAsync ( HttpContext context )
923
+ {
924
+ return new ( result : null ) ;
925
+ }
926
+ }
927
+
928
+ private class BindAsyncFromClassAndInterface
929
+ : BaseBindAsync < BindAsyncFromClassAndInterface > ,
930
+ IBindAsync < BindAsyncFromClassAndInterface >
931
+ {
932
+ }
933
+
934
+ private class BindAsyncFromMultipleInterfaces
935
+ : IBindAsync < BindAsyncFromMultipleInterfaces > ,
936
+ IBindAsync2 < BindAsyncFromMultipleInterfaces >
937
+ {
938
+ }
939
+
940
+ // using wrong T on purpose
941
+ private class BindAsyncWrongTypeFromInterface : IBindAsync < BindAsyncFromInterface >
942
+ {
943
+ }
944
+
945
+ private interface IBindAsyncWithParameterInfo < T >
946
+ {
947
+ static ValueTask < T ? > BindAsync ( HttpContext context , ParameterInfo parameter )
948
+ {
949
+ return new ( default ( T ) ) ;
950
+ }
951
+ }
952
+
953
+ private class BindAsyncFromInterfaceWithParameterInfo : IBindAsync < BindAsyncFromInterfaceWithParameterInfo >
954
+ {
955
+ }
956
+
774
957
private class MockParameterInfo : ParameterInfo
775
958
{
776
959
public MockParameterInfo ( Type type , string name )
0 commit comments