@@ -41,6 +41,11 @@ public sealed partial class ObservableAsPropertyGenerator
4141 attributeData . TryGetNamedArgument ( "UseProtected" , out bool useProtected ) ;
4242 var useProtectedModifier = useProtected ? "protected" : "private" ;
4343
44+ token . ThrowIfCancellationRequested ( ) ;
45+
46+ // Get the can ReadOnly member, if any
47+ attributeData . TryGetNamedArgument ( "ReadOnly" , out bool ? isReadonly ) ;
48+
4449 token . ThrowIfCancellationRequested ( ) ;
4550 var compilation = context . SemanticModel . Compilation ;
4651
@@ -106,6 +111,7 @@ public sealed partial class ObservableAsPropertyGenerator
106111 isNullableType ,
107112 false ,
108113 propertyAttributes ,
114+ string . Empty ,
109115 useProtectedModifier ,
110116 initialValue ) ,
111117 diagnostics . ToImmutable ( ) ) ;
@@ -131,6 +137,7 @@ public sealed partial class ObservableAsPropertyGenerator
131137
132138 var observableType = string . Empty ;
133139 var isNullableType = false ;
140+ var isPartialProperty = false ;
134141
135142 token . ThrowIfCancellationRequested ( ) ;
136143 context . GetForwardedAttributes (
@@ -173,10 +180,9 @@ public sealed partial class ObservableAsPropertyGenerator
173180
174181 token . ThrowIfCancellationRequested ( ) ;
175182
176- var inheritance = propertySymbol . IsVirtual ? " virtual" : propertySymbol . IsOverride ? " override" : string . Empty ;
183+ isPartialProperty = true ;
177184
178- // Get the can ReadOnly member, if any
179- attributeData . TryGetNamedArgument ( "ReadOnly" , out bool ? isReadonly ) ;
185+ var inheritance = propertySymbol . IsVirtual ? " virtual" : propertySymbol . IsOverride ? " override" : string . Empty ;
180186
181187 token . ThrowIfCancellationRequested ( ) ;
182188
@@ -215,6 +221,12 @@ public sealed partial class ObservableAsPropertyGenerator
215221 }
216222#endif
217223
224+ var isReadOnlyString = string . Empty ;
225+ if ( isPartialProperty )
226+ {
227+ isReadOnlyString = isReadonly == false ? string . Empty : "readonly" ;
228+ }
229+
218230 // Get the containing type info
219231 var targetInfo = TargetInfo . From ( propertySymbol . ContainingType ) ;
220232
@@ -229,6 +241,7 @@ public sealed partial class ObservableAsPropertyGenerator
229241 isNullableType ,
230242 true ,
231243 propertyAttributes ,
244+ isReadOnlyString ,
232245 useProtectedModifier ,
233246 initialValue ) ,
234247 diagnostics . ToImmutable ( ) ) ;
@@ -307,12 +320,20 @@ private static string GetPropertySyntax(ObservableMethodInfo propertyInfo)
307320 propertyType = propertyInfo . PartialPropertyType ;
308321 }
309322
323+ var helperTypeName = $ "{ propertyInfo . AccessModifier } ReactiveUI.ObservableAsPropertyHelper<{ propertyType } >?";
324+
325+ // If the property is readonly, we need to change the helper to be non-nullable
326+ if ( propertyInfo . IsReadOnly == "readonly" )
327+ {
328+ helperTypeName = $ "{ propertyInfo . AccessModifier } readonly ReactiveUI.ObservableAsPropertyHelper<{ propertyType } >";
329+ }
330+
310331 return $$ """
311332/// <inheritdoc cref="{{ propertyInfo . PropertyName }} "/>
312333 private {{ propertyType }} {{ getterFieldIdentifierName }} {{ initialValue }} ;
313334
314335 /// <inheritdoc cref="{{ getterFieldIdentifierName }} Helper"/>
315- {{ propertyInfo . AccessModifier }} ReactiveUI.ObservableAsPropertyHelper< {{ propertyType }} >? {{ getterFieldIdentifierName }} Helper;
336+ {{ helperTypeName }} {{ getterFieldIdentifierName }} Helper;
316337
317338 /// <inheritdoc cref="{{ getterFieldIdentifierName }} "/>
318339 {{ propertyAttributes }}
0 commit comments