|
8 | 8 | using System.ComponentModel.DataAnnotations;
|
9 | 9 | using System.Linq;
|
10 | 10 | using System.Reflection;
|
| 11 | +using System.Runtime.CompilerServices; |
11 | 12 | using System.Threading.Tasks;
|
12 | 13 | using CommunityToolkit.Mvvm.ComponentModel;
|
13 | 14 | using CommunityToolkit.Mvvm.Input;
|
@@ -417,6 +418,103 @@ private void Test_AlsoBroadcastChange_Test<T>(Func<IMessenger, T> factory, Actio
|
417 | 418 | Assert.AreEqual(propertyName, messages[0].Message.PropertyName);
|
418 | 419 | }
|
419 | 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 bug in the NullabilityInfo API in .NET 6. |
| 513 | + // This has been fixed for .NET 7: https://github.com/dotnet/runtime/pull/63556. The test |
| 514 | + // cases above can be uncommented when the .NET 7 target (or a more recent version) is added. |
| 515 | + } |
| 516 | +#endif |
| 517 | + |
420 | 518 | public partial class SampleModel : ObservableObject
|
421 | 519 | {
|
422 | 520 | /// <summary>
|
@@ -704,4 +802,22 @@ public BroadcastingViewModelWithInheritedAttribute(IMessenger messenger)
|
704 | 802 | [AlsoBroadcastChange]
|
705 | 803 | private string? name2;
|
706 | 804 | }
|
| 805 | + |
| 806 | +#if NET6_0_OR_GREATER |
| 807 | + private partial class NullableRepro : ObservableObject |
| 808 | + { |
| 809 | + [ObservableProperty] |
| 810 | + private List<string?>? nullableList; |
| 811 | + |
| 812 | + [ObservableProperty] |
| 813 | + private Foo<Foo<string?, int>.Bar<object?>?, StrongBox<Foo<int, string?>.Bar<object>?>?>? nullableMess; |
| 814 | + } |
| 815 | + |
| 816 | + private class Foo<T1, T2> |
| 817 | + { |
| 818 | + public class Bar<T> |
| 819 | + { |
| 820 | + } |
| 821 | + } |
| 822 | +#endif |
707 | 823 | }
|
0 commit comments