Skip to content

Commit 353e0f8

Browse files
committed
Add AlsoNotify support to ReactiveAttribute and generator
Introduces the AlsoNotify parameter to the ReactiveAttribute, allowing additional property change notifications to be specified. Updates the source generator to handle AlsoNotify, emitting RaisePropertyChanged calls for the specified properties in generated setters. Includes new and updated tests to verify the new behavior, and updates documentation to describe the new feature.
1 parent f8a5eca commit 353e0f8

File tree

30 files changed

+436
-23
lines changed

30 files changed

+436
-23
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ ReactiveUI Source Generators automatically generate ReactiveUI objects to stream
2828
- `[Reactive(SetModifier = AccessModifier.Protected)]` With field and access modifiers, (Not Required for partial properties, configure set accessor with the property decalaration).
2929
- `[Reactive(Inheritance = InheritanceModifier.Virtual)]` With field and access modifiers. This will generate a virtual property.
3030
- `[Reactive(UseRequired = true)]` With field and access modifiers. This will generate a required property, (Not Required for partial properties, use required keyword for property decalaration).
31+
- `[Reactive(nameof(RaiseProperty1), nameof(RaiseProperty2))]` With field and property changed notification for additional properties.
3132
- `[ObservableAsProperty]` With field, method, Observable property and partial property support (C# 13 Visual Studio Version 17.12.0)
3233
- `[ObservableAsProperty(ReadOnly = false)]` Removes readonly keyword from the generated helper field
3334
- `[ObservableAsProperty(PropertyName = "ReadOnlyPropertyName")]`
@@ -91,6 +92,9 @@ Generates a derived list from a `ReadOnlyObservableCollection` backing field.
9192
### `[ReactiveCollection]`
9293
Generates property changed notifications on add, remove, and new actions on an `ObservableCollection` backing field.
9394

95+
### `[IReactiveObject]`
96+
Generates `IReactiveObject` implementation for classes not able to inherit from `ReactiveObject`.
97+
9498
## Historical Approach
9599

96100
### Read-Write Properties
@@ -685,7 +689,7 @@ public partial class MyReactiveClass : ReactiveObject
685689
}
686690
```
687691

688-
### ReactiveObject implementation for classes not able to inherit from ReactiveObject
692+
### IReactiveObject implementation for classes not able to inherit from ReactiveObject
689693
```csharp
690694
using ReactiveUI;
691695
using ReactiveUI.SourceGenerators;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//HintName: ReactiveUI.SourceGenerators.AccessModifier.g.cs
2+
// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved.
3+
// Licensed to the .NET Foundation under one or more agreements.
4+
// The .NET Foundation licenses this file to you under the MIT license.
5+
// See the LICENSE file in the project root for full license information.
6+
7+
// <auto-generated/>
8+
#pragma warning disable
9+
#nullable enable
10+
namespace ReactiveUI.SourceGenerators;
11+
12+
/// <summary>
13+
/// AccessModifier.
14+
/// </summary>
15+
internal enum AccessModifier
16+
{
17+
Public,
18+
Protected,
19+
Internal,
20+
Private,
21+
InternalProtected,
22+
PrivateProtected,
23+
Init,
24+
}
25+
26+
/// <summary>
27+
/// Property Access Modifier.
28+
/// </summary>
29+
internal enum PropertyAccessModifier
30+
{
31+
Public,
32+
Protected,
33+
Internal,
34+
Private,
35+
InternalProtected,
36+
PrivateProtected,
37+
}
38+
39+
/// <summary>
40+
/// InheritanceModifier.
41+
/// </summary>
42+
internal enum InheritanceModifier
43+
{
44+
None,
45+
Virtual,
46+
Override,
47+
New,
48+
}
49+
50+
internal enum SplatRegistrationType
51+
{
52+
None,
53+
LazySingleton,
54+
Constant,
55+
PerRequest,
56+
}
57+
#nullable restore
58+
#pragma warning restore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//HintName: ReactiveUI.SourceGenerators.ReactiveAttribute.g.cs
2+
// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved.
3+
// Licensed to the .NET Foundation under one or more agreements.
4+
// The .NET Foundation licenses this file to you under the MIT license.
5+
// See the LICENSE file in the project root for full license information.
6+
7+
// <auto-generated/>
8+
#pragma warning disable
9+
#nullable enable
10+
namespace ReactiveUI.SourceGenerators;
11+
12+
/// <summary>
13+
/// ReactiveAttribute.
14+
/// </summary>
15+
/// <seealso cref="Attribute" />
16+
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
17+
internal sealed class ReactiveAttribute : global::System.Attribute
18+
{
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="ReactiveAttribute"/> class.
21+
/// </summary>
22+
public ReactiveAttribute(params string[] alsoNotify)
23+
{
24+
AlsoNotify = alsoNotify;
25+
}
26+
27+
/// <summary>
28+
/// Gets the AccessModifier of the set property.
29+
/// </summary>
30+
/// <value>
31+
/// The AccessModifier of the set property.
32+
/// </value>
33+
public AccessModifier SetModifier { get; init; }
34+
35+
/// <summary>
36+
/// Gets the InheritanceModifier of the property.
37+
/// </sumary>
38+
public InheritanceModifier Inheritance { get; init; }
39+
40+
/// <summary>
41+
/// Use Required attribute to indicate that the property is required.
42+
/// </summary>
43+
public bool UseRequired { get; init; }
44+
45+
/// <summary>
46+
/// Gets the AlsoNotify properties to raise change notifications for.
47+
/// </summary>
48+
public string[]? AlsoNotify { get; }
49+
}
50+
#nullable restore
51+
#pragma warning restore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//HintName: TestNs.TestVM.Properties.g.cs
2+
// <auto-generated/>
3+
using ReactiveUI;
4+
5+
#pragma warning disable
6+
#nullable enable
7+
8+
namespace TestNs
9+
{
10+
11+
public partial class TestVM
12+
{
13+
14+
/// <inheritdoc cref="_test4"/>
15+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
16+
public int Test4
17+
{
18+
get => _test4;
19+
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test4")]
20+
set
21+
{
22+
this.RaiseAndSetIfChanged(ref _test4, value);
23+
}
24+
}
25+
}
26+
}
27+
#nullable restore
28+
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators;
1616
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
1717
internal sealed class ReactiveAttribute : global::System.Attribute
1818
{
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="ReactiveAttribute"/> class.
21+
/// </summary>
22+
public ReactiveAttribute(params string[] alsoNotify)
23+
{
24+
AlsoNotify = alsoNotify;
25+
}
26+
1927
/// <summary>
2028
/// Gets the AccessModifier of the set property.
2129
/// </summary>
@@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute
3341
/// Use Required attribute to indicate that the property is required.
3442
/// </summary>
3543
public bool UseRequired { get; init; }
44+
45+
/// <summary>
46+
/// Gets the AlsoNotify properties to raise change notifications for.
47+
/// </summary>
48+
public string[]? AlsoNotify { get; }
3649
}
3750
#nullable restore
3851
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators;
1616
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
1717
internal sealed class ReactiveAttribute : global::System.Attribute
1818
{
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="ReactiveAttribute"/> class.
21+
/// </summary>
22+
public ReactiveAttribute(params string[] alsoNotify)
23+
{
24+
AlsoNotify = alsoNotify;
25+
}
26+
1927
/// <summary>
2028
/// Gets the AccessModifier of the set property.
2129
/// </summary>
@@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute
3341
/// Use Required attribute to indicate that the property is required.
3442
/// </summary>
3543
public bool UseRequired { get; init; }
44+
45+
/// <summary>
46+
/// Gets the AlsoNotify properties to raise change notifications for.
47+
/// </summary>
48+
public string[]? AlsoNotify { get; }
3649
}
3750
#nullable restore
3851
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ public int Test3
1919
{
2020
get => _test3;
2121
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test3")]
22-
set => this.RaiseAndSetIfChanged(ref _test3, value);
22+
set
23+
{
24+
this.RaiseAndSetIfChanged(ref _test3, value);
25+
}
2326
}
2427
}
2528
}

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators;
1616
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
1717
internal sealed class ReactiveAttribute : global::System.Attribute
1818
{
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="ReactiveAttribute"/> class.
21+
/// </summary>
22+
public ReactiveAttribute(params string[] alsoNotify)
23+
{
24+
AlsoNotify = alsoNotify;
25+
}
26+
1927
/// <summary>
2028
/// Gets the AccessModifier of the set property.
2129
/// </summary>
@@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute
3341
/// Use Required attribute to indicate that the property is required.
3442
/// </summary>
3543
public bool UseRequired { get; init; }
44+
45+
/// <summary>
46+
/// Gets the AlsoNotify properties to raise change notifications for.
47+
/// </summary>
48+
public string[]? AlsoNotify { get; }
3649
}
3750
#nullable restore
3851
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ public int Test1
1717
{
1818
get => _test1;
1919
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test1")]
20-
set => this.RaiseAndSetIfChanged(ref _test1, value);
20+
set
21+
{
22+
this.RaiseAndSetIfChanged(ref _test1, value);
23+
}
2124
}
2225
}
2326
}

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators;
1616
[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
1717
internal sealed class ReactiveAttribute : global::System.Attribute
1818
{
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="ReactiveAttribute"/> class.
21+
/// </summary>
22+
public ReactiveAttribute(params string[] alsoNotify)
23+
{
24+
AlsoNotify = alsoNotify;
25+
}
26+
1927
/// <summary>
2028
/// Gets the AccessModifier of the set property.
2129
/// </summary>
@@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute
3341
/// Use Required attribute to indicate that the property is required.
3442
/// </summary>
3543
public bool UseRequired { get; init; }
44+
45+
/// <summary>
46+
/// Gets the AlsoNotify properties to raise change notifications for.
47+
/// </summary>
48+
public string[]? AlsoNotify { get; }
3649
}
3750
#nullable restore
3851
#pragma warning restore

0 commit comments

Comments
 (0)