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.
this.Name != null
as well in theIsValid
property to make it easier for the compiler to see thenull
check (and not hide behindstring.IsNullOrWhiteSpace()
? – Hanshansardis
?if( form.FaceColour is string colour && colour.Equals( "Blue" )) { Console.WriteLine(colour); }
– Knisleyform is { IsValid: true, FaceColour: "Blue" }
would work too since it's all constants... Though I admit it's not what's being asked, I just like how it looks – Vittoriois { Prop: value }
syntax, how do you specify anIComparer
? or is it always bound toObject.Equals(Object)
- or does it useIEquatable<T>
if available? – Merissais
essentially) then it would become possible to specify a Comparer. But for now... You can't – Vittorio