Supposing I have a class like this:
public class BridgeFormModel
{
[Required]
[Display( Name = "What is your name?" )]
public String? Name { get; set; }
[Required]
[Display( Name = "What is your quest?" )]
public String? Quest { get; set; }
[Required]
[Display( Name = "What is your favourite colour?" )]
public String? FaveColour { get; set; }
[BindNever]
public Boolean IsValid =>
!String.IsNullOrWhiteSpace( this.Name ) &&
!String.IsNullOrWhiteSpace( this.Quest ) &&
!String.IsNullOrWhiteSpace( this.FaveColour );
}
Currently the C# 8.0 and C# 9.0 compiler will not infer that Name is not-null when it knows IsValid is true:
public IActionResult CrossTheBridge( BridgeFormModel form )
{
if( form.IsValid )
{
if( form.FaveColour.Equals( "Blue" ) ) // Warning: `form.FaveColour` may be null here
{
Console.WriteLine( "Right, off you go" )
}
}
}
So we have to either assert form.FaveColour! - or do this instead:
public IActionResult CrossTheBridge( BridgeFormModel form )
{
if( form.IsValid && form.FaveColour != null && form.Name != null && form.Quest != null )
{
if( form.FaveColour.Equals( "Blue" ) )
{
Console.WriteLine( "Right, off you go" )
}
}
}
We have the [NullWhen] and [NotNullWhen] attributes, but those only apply to method parameters, not other properties on the same object instance.
This wouldn't be an issue if C# still supported code-contracts, but alas, here we are... is there any way to inform the C# compiler of nullability (and other state-invariants?) based on a property?
I'd like to be able to do something like this:
public class BridgeFormModel
{
[NotNullWhenPropertyIsTrue( nameof(IsValid) )]
[Required]
[Display( Name = "What is your name?" )]
public String? Name { get; set; }
[NotNullWhenPropertyIsTrue( nameof(IsValid) )]
[Required]
[Display( Name = "What is your quest?" )]
public String? Quest { get; set; }
[NotNullWhenPropertyIsTrue( nameof(IsValid) )]
[Required]
[Display( Name = "What is your favourite colour?" )]
public String? FaveColour { get; set; }
[BindNever]
public Boolean IsValid =>
!String.IsNullOrWhiteSpace( this.Name ) &&
!String.IsNullOrWhiteSpace( this.Quest ) &&
!String.IsNullOrWhiteSpace( this.FaveColour );
}
As NotNullWhenPropertyIsTrue is not a real attribute, I'm wondering if there's some way to write a Roslyn extension or analyser that can implement the necessary logic - or provide null-safety assertions to Roslyn.