Properties that use the field
keyword will not have warnings such as
For properties that have a get
accessor with a body and which use field
, analyze using this pseudocode, providing the same warnings as we would with accessors as local functions.
void Prop()
{
T? field = default;
// If there is an initializer:
field = ...init-expr...
// If there is a constructor assignment:
set(...non-null-value...);
while (true)
{
if (...non-constant-value...)
get()
else
// If there truly is a setter on the property. Otherwise, this is elided.
set(...non-null-value...);
}
return;
T get() { ...get-body... }
void set(T value) { ...set-body... }
}
public C()
{
T? field = default;
// If there is an initializer:
field = ...init-expr...
...ctor-statements, replacing property accessor calls with get/set local function calls...
while (true)
{
if (...non-constant-value...)
get()
else
// If there truly is a setter on the property. Otherwise, this is elided.
set(...non-null-value...);
}
return;
T get() { ...get-body... }
void set(T value) { ...set-body... }
}
Example:
class C : ViewModel
{
// No warnings
public C()
{
AutoProp = "";
AutoGetterProp = "";
}
// Squiggle on 'C': Non-nullable property AutoProp must contain a non-null value when exiting the constructor.
public C(int p) { }
// No warnings
public string AutoProp { get; set; }
// Squiggle on 'get': Possible null reference return when the `C(int p)` constructor is used
public string AutoGetterProp { get; set => Set(ref field, value); }
// Squiggle on 'field': Possible null reference return when the `C(int p)` constructor is used
public string ManualFieldProp { get => field; set => Set(ref field, value); }
// No warnings
public string InitializedProp { get => field; set => Set(ref field, value); } = "";
// No warnings
public required string RequiredProp { get => field; set => Set(ref field, value); }
}
abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected bool Set<T>(ref T field, T value, [CallerMemberName] string? propertyName = null);
}
public string IntermediateStateProp { get => field; set => _myOldField = value; }
public string ConditionalProp { get => field; set { if (cond) field = value; } }