@@ -418,6 +418,105 @@ private void Test_AlsoBroadcastChange_Test<T>(Func<IMessenger, T> factory, Actio
418
418
Assert . AreEqual ( propertyName , messages [ 0 ] . Message . PropertyName ) ;
419
419
}
420
420
421
+ #if NET6_0_OR_GREATER
422
+ // See https://github.com/CommunityToolkit/dotnet/issues/155
423
+ [ TestMethod ]
424
+ public void Test_ObservableProperty_NullabilityAnnotations_Simple ( )
425
+ {
426
+ // List<string?>?
427
+ NullabilityInfoContext context = new ( ) ;
428
+ NullabilityInfo info = context . Create ( typeof ( NullableRepro ) . GetProperty ( nameof ( NullableRepro . NullableList ) ) ! ) ;
429
+
430
+ Assert . AreEqual ( typeof ( List < string > ) , info . Type ) ;
431
+ Assert . AreEqual ( NullabilityState . Nullable , info . ReadState ) ;
432
+ Assert . AreEqual ( NullabilityState . Nullable , info . WriteState ) ;
433
+ Assert . AreEqual ( 1 , info . GenericTypeArguments . Length ) ;
434
+
435
+ NullabilityInfo elementInfo = info . GenericTypeArguments [ 0 ] ;
436
+
437
+ Assert . AreEqual ( typeof ( string ) , elementInfo . Type ) ;
438
+ Assert . AreEqual ( NullabilityState . Nullable , elementInfo . ReadState ) ;
439
+ Assert . AreEqual ( NullabilityState . Nullable , elementInfo . WriteState ) ;
440
+ }
441
+
442
+ // See https://github.com/CommunityToolkit/dotnet/issues/155
443
+ [ TestMethod ]
444
+ public void Test_ObservableProperty_NullabilityAnnotations_Complex ( )
445
+ {
446
+ // Foo<Foo<string?, int>.Bar<object?>?, StrongBox<Foo<int, string?>.Bar<object>?>?>?
447
+ NullabilityInfoContext context = new ( ) ;
448
+ NullabilityInfo info = context . Create ( typeof ( NullableRepro ) . GetProperty ( nameof ( NullableRepro . NullableMess ) ) ! ) ;
449
+
450
+ Assert . AreEqual ( typeof ( Foo < Foo < string ? , int > . Bar < object ? > ? , StrongBox < Foo < int , string ? > . Bar < object > ? > ? > ) , info . Type ) ;
451
+ Assert . AreEqual ( NullabilityState . Nullable , info . ReadState ) ;
452
+ Assert . AreEqual ( NullabilityState . Nullable , info . WriteState ) ;
453
+ Assert . AreEqual ( 2 , info . GenericTypeArguments . Length ) ;
454
+
455
+ NullabilityInfo leftInfo = info . GenericTypeArguments [ 0 ] ;
456
+
457
+ Assert . AreEqual ( typeof ( Foo < string ? , int > . Bar < object ? > ) , leftInfo . Type ) ;
458
+ Assert . AreEqual ( NullabilityState . Nullable , leftInfo . ReadState ) ;
459
+ Assert . AreEqual ( NullabilityState . Nullable , leftInfo . WriteState ) ;
460
+ Assert . AreEqual ( 3 , leftInfo . GenericTypeArguments . Length ) ;
461
+
462
+ NullabilityInfo leftInfo0 = leftInfo . GenericTypeArguments [ 0 ] ;
463
+
464
+ Assert . AreEqual ( typeof ( string ) , leftInfo0 . Type ) ;
465
+ Assert . AreEqual ( NullabilityState . Nullable , leftInfo0 . ReadState ) ;
466
+ Assert . AreEqual ( NullabilityState . Nullable , leftInfo0 . WriteState ) ;
467
+
468
+ NullabilityInfo leftInfo1 = leftInfo . GenericTypeArguments [ 1 ] ;
469
+
470
+ Assert . AreEqual ( typeof ( int ) , leftInfo1 . Type ) ;
471
+ Assert . AreEqual ( NullabilityState . NotNull , leftInfo1 . ReadState ) ;
472
+ Assert . AreEqual ( NullabilityState . NotNull , leftInfo1 . WriteState ) ;
473
+
474
+ NullabilityInfo leftInfo2 = leftInfo . GenericTypeArguments [ 2 ] ;
475
+
476
+ Assert . AreEqual ( typeof ( object ) , leftInfo2 . Type ) ;
477
+ Assert . AreEqual ( NullabilityState . Nullable , leftInfo2 . ReadState ) ;
478
+ Assert . AreEqual ( NullabilityState . Nullable , leftInfo2 . WriteState ) ;
479
+
480
+ NullabilityInfo rightInfo = info . GenericTypeArguments [ 1 ] ;
481
+
482
+ Assert . AreEqual ( typeof ( StrongBox < Foo < int , string ? > . Bar < object > ? > ) , rightInfo . Type ) ;
483
+ Assert . AreEqual ( NullabilityState . Nullable , rightInfo . ReadState ) ;
484
+ Assert . AreEqual ( NullabilityState . Nullable , rightInfo . WriteState ) ;
485
+ Assert . AreEqual ( 1 , rightInfo . GenericTypeArguments . Length ) ;
486
+
487
+ NullabilityInfo rightInnerInfo = rightInfo . GenericTypeArguments [ 0 ] ;
488
+
489
+ Assert . AreEqual ( typeof ( Foo < int , string ? > . Bar < object > ) , rightInnerInfo . Type ) ;
490
+ Assert . AreEqual ( NullabilityState . Nullable , rightInnerInfo . ReadState ) ;
491
+ Assert . AreEqual ( NullabilityState . Nullable , rightInnerInfo . WriteState ) ;
492
+ Assert . AreEqual ( 3 , rightInnerInfo . GenericTypeArguments . Length ) ;
493
+
494
+ NullabilityInfo rightInfo0 = rightInnerInfo . GenericTypeArguments [ 0 ] ;
495
+
496
+ Assert . AreEqual ( typeof ( int ) , rightInfo0 . Type ) ;
497
+ Assert . AreEqual ( NullabilityState . NotNull , rightInfo0 . ReadState ) ;
498
+ Assert . AreEqual ( NullabilityState . NotNull , rightInfo0 . WriteState ) ;
499
+
500
+ NullabilityInfo rightInfo1 = rightInnerInfo . GenericTypeArguments [ 1 ] ;
501
+
502
+ Assert . AreEqual ( typeof ( string ) , rightInfo1 . Type ) ;
503
+ Assert . AreEqual ( NullabilityState . Nullable , rightInfo1 . ReadState ) ;
504
+ Assert . AreEqual ( NullabilityState . Nullable , rightInfo1 . WriteState ) ;
505
+
506
+ NullabilityInfo rightInfo2 = rightInnerInfo . GenericTypeArguments [ 2 ] ;
507
+
508
+ Assert . AreEqual ( typeof ( object ) , rightInfo2 . Type ) ;
509
+ //Assert.AreEqual(NullabilityState.NotNull, rightInfo2.ReadState);
510
+ //Assert.AreEqual(NullabilityState.NotNull, rightInfo2.WriteState);
511
+
512
+ // The commented out lines are to work around a weird behavior of the NullabilityInfo API there.
513
+ // Arguably we're pushing them a bit far here, but it's fine. Even with those cases commented out,
514
+ // the test is already more than enough, plus we can also double check the behavior by looking at
515
+ // the generated code. Thoe lines can be uncommented once the behavior is either clarified, or if
516
+ // it happens to be a bug which is then fixed in a future version of .NET, once we upgrade as well.
517
+ }
518
+ #endif
519
+
421
520
public partial class SampleModel : ObservableObject
422
521
{
423
522
/// <summary>
@@ -705,4 +804,22 @@ public BroadcastingViewModelWithInheritedAttribute(IMessenger messenger)
705
804
[ AlsoBroadcastChange ]
706
805
private string ? name2 ;
707
806
}
807
+
808
+ #if NET6_0_OR_GREATER
809
+ private partial class NullableRepro : ObservableObject
810
+ {
811
+ [ ObservableProperty ]
812
+ private List < string ? > ? nullableList ;
813
+
814
+ [ ObservableProperty ]
815
+ private Foo < Foo < string ? , int > . Bar < object ? > ? , StrongBox < Foo < int , string ? > . Bar < object > ? > ? > ? nullableMess ;
816
+ }
817
+
818
+ private class Foo < T1 , T2 >
819
+ {
820
+ public class Bar < T >
821
+ {
822
+ }
823
+ }
824
+ #endif
708
825
}
0 commit comments