Category Archives: Programming

WPF Validation methods

How do we handle validation in WPF ?

Before we begin…

Let’s start by looking at the view model we’re going to work on and write our validation code for – this is a very simple model with a single property “Number” which could ofcourse represent anything you like.

public class NumberViewModel : INotifyPropertyChanged
{
   // standard implementation of INotifyPropertyChanged removed 
   private int number;

   public int Number
   {
      get { return number; }
      set
      {
         if (number != value)
         {
            number = value;
            OnPropertyChanged("Number");
         }
      }
   }
}

You can assume that OnPropertyChanged fires the INotifyPropertyChanged.PropertyChanged event, but I’ve removed the implementation for brevity.

We’ll be equally simple with our UI, which has the following XAML

<TextBox Text="{Binding Number}" />

Good old IDataErrorInfo

So if you’ve used IDataErrorInfo in WinForms, this will be very familiar to you. We can simple implement the IDataErrorInfo interface in our NumberViewModel and add something like the following

string IDataErrorInfo.Error
{
   get { return null; }
}

string IDataErrorInfo.this[string columnName]
{
   get
   {
      if (columnName == "Number")
      {
         if (number < 0)
            return "Number must be greater or equal to 0";
      }
      return null;
   }
}

To get the WPF binding to actually interact with the IDataErrorInfo we need to change the XAML to look like this

<TextBox Text="{Binding Number, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />

In the above our IDataErrorInfo.this indexer will be called each time the property changes, at which time we can handle the validation either within the view model or ofcourse via some other validation rule class.

ValidationRule validation

An alternative to the IDataErrorInfo route for validation are ValidationRules. A ValidateRule implementation of the previous validator is listed below

public class PostiveValidationRule : ValidationRule
{
   public override ValidationResult Validate(object value, CultureInfo cultureInfo)
   {
      int result;
      if (value != null && Int32.TryParse(value.ToString(), out result))
      {
         if (result < 0)
            return new ValidationResult(
               false, "Number must be greater or equal to 0");
      }
      return ValidationResult.ValidResult;
   }
}

To use the above rule in XAML we write the following

<TextBox>
   <TextBox.Text>
      <Binding Path="Number" 
            ValidatesOnDataErrors="True" 
            UpdateSourceTrigger="PropertyChanged">
         <Binding.ValidationRules>
            <validators:PostiveValidationRule />
         </Binding.ValidationRules>
      </Binding>
   </TextBox.Text>
</TextBox>

The ValidationRules (as the pluralism suggests) can have multiple rules listed.

INotifyDataErrorInfo

.NET 4.5 included the INotifyDataErrorInfo. This allows us to validate in a more asynchronous way in that we can raise the ErrorsChanged event when we have errors and report them so that the binding engine can then call the GetErrors method to get the list of errors.

BindingGroup

The previously highlighted validation methods tend to be aimed more at a specific view model, but what if our view is made up of multiple view models and we want to validate across them all. Then we can look to use the BindingGroup.

The BindingGroup creates a relationship between multiple bindings, which can be validated and updated together.

To put it another way, the BindingGroup allows us to validate a group of bindings at the same time. Our sample only has a view model but if we had multiple view models it could validate all the items that make up a BindingGroup. Let’s look at how we could create a BindingGroup.

<StackPanel>
   <StackPanel.BindingGroup>
     <BindingGroup Name="ValidDataGroup">
        <BindingGroup.ValidationRules>
           <validators:ValidDataValidationRule />
        </BindingGroup.ValidationRules>
     </BindingGroup>
   </StackPanel.BindingGroup>
   <TextBox Text="{Binding Text, ValidatesOnDataErrors=True, BindingGroupName=ValidDataGroup}" />

We give the BindingGroup a name and then we can assign this name to the various bindings – in this example we don’t actually need to use the group name on the textbox as it will be used within the validation, but hopefully you can see how the syntax would look.

Now let’s take a look at the ValidDataValidationRule code

public class ValidDataValidationRule : ValidationRule
{
   public override ValidationResult Validate(object value, CultureInfo cultureInfo)
   {
      var bindingGroup = (BindingGroup)value;
      if (bindingGroup != null)
      {
         var numberViewModel = bindingGroup.Items[0] as NumberViewModel;
         if (numberViewModel != null)
         {
            if (numberViewModel.Number < 0)
            {
               return new ValidationResult(
                  false, 
                  "Number must be greater or equal to 0");
            }
         } 
      }
      return ValidationResult.ValidResult;
   }		
}

The Items collection will contain the various groups that have been assigned the BindingGroup name and then we can validate across all the data contexts.

Unfortunately, this doesn’t just happen magically. Instead, we need to invoke it. If we give the name DataElement to the StackPanel and for the sake of simplicity we add a button with a Click handler, we can then call the BindingGroup’s CommitEdit method to force validation, i.e.

DataElement.BindingGroup.CommitEdit();

Note: The default style is to draw a read line around the control which fails validation, if you’ve placed the StackPanel as the top level Window you might need to add a margin to see the red border.

ValidationStep

On both the BindingGroup and ValidationRule we can set the ValidationStep property. This allows us to tell the binding mechanism at what stage it should invoke the validation rule.

This can be set to one of four values

CommittedValue: Runs the ValidationRule after the value has been committed to the source.
ConvertedProposedValue: Runs the Validation rule after a value is converetd.
RawProposedValue: Runs the validation before any conversion occurs.
UpdatedValue: Runs the ValidationRule after the source is updated.

Note: The above definitions were taken from ValidationStep Enumeration

Data annotations

Finally, let’s take a look at data annotations or more specifically validation attributes that are part of the System.ComplonentMode.DataAnnotations namespace.

With the data annotations we can apply attributes to a class or its members which denote the validation rules to be used. For example

public class ValidationModel
{
   [Required(ErrorMessage = "First name is a required field")]
   public string FirstName { get; set; }
}

In this example we’ve removed the get/set actual implementation, for brevity.

Now this code requires use to write code to validation the property, for example in our setter we might have code like this

var validationContext = new ValidationContext(this, null, null);
validationContext.MemberName = nameof(FirstName);
Validator.ValidateProperty(value, validationContext);

Enhancing the user interface

So far, I’ve not done anything to make the user experience any better regarding validation, i.e. you will only see a red border around our text box, but this doesn’t really help the user identify what’s gone wrong, so now let’s look at how we might change our UI to better reflect the validation failures.

Ofcourse, we’ve been busy creating error messages, but so far not shown them in the UI, so we could use something like the following

<TextBox Text="{Binding Number, 
      UpdateSourceTrigger=PropertyChanged, 
      ValidatesOnDataErrors=True}">
   <Validation.ErrorTemplate>
      <ControlTemplate>
         <StackPanel>
            <AdornedElementPlaceholder />
            <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
         </StackPanel>
      </ControlTemplate>
   </Validation.ErrorTemplate>
</TextBox>

This will display the first error message just beneath the TextBox.

Another alternative is to style the text box with a trigger against the Validation.HasError property

<Style TargetType="{x:Type TextBox}">
   <Style.Triggers>
      <Trigger Property="Validation.HasError" Value="True">
         <Setter Property="Background" Value="Red" />
          <Setter Property="ToolTip"
             Value="{Binding RelativeSource={x:Static RelativeSource.Self},
             Path=(Validation.Errors)[0].ErrorContent}"/>
      </Trigger>
   </Style.Triggers>
</Style>

References

Data validation in WPF
Using BindingGroups For Greater Control Over Input Validation
BindingGroups For Total View Validation
WPF 3.5 SP1 Feature: BindingGroups with Item-level Validation
Validation in Windows Presentation Foundation

Assembly Redirects of Strong-Named Assemblies

It’s unfortunately all too often recently that I’ve need to redirect assemblies, so I thought I’d write a quick post on the subject.

Occasionally you may have a situation where one library (let’s call it Lib1) you’re using in .NET relies on a specific version of a another, strong-named, library (let’s call it Lib2). Then you go and update the library Lib2 to a newer version and things break. Lib1 expects a specific version of Lib2 and we’re not supplying it.

Simple open/add your App.config file – we then add something like this

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Lib2" publicKeyToken="Lib2-PublicToken" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-1.0.0.0" newVersion="2.0.0.0" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

Obviously you replace Lib2 with the assembly that your code is dependent upon, you’ll also need to supply the publicKeyToken. The oldVersion is a range of versions from 0.0.0.0 to 1.0.0.0 in this case, meaning anything that required this assembly version 0 to 1 should now use version 2 (2.0.0.0).

When adding further assembly redirects we of just supply further dependentAssembly elements.

To find the publicKeyToken we can use the Strong Name utility (sn.exe) which can be found here (for example)

"%ProgramFiles%\\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\sn.exe" -T <assemblyName>

We can also find such information via the likes of ILSpy, by clicking on the assembly and in the right hand pane we’ll have information such as

// Microsoft.VisualC, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

Further Reading

Strong-Named Assemblies
Redirecting Assembly Versions

Computational Expressions, the “standard” methods

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

So in a previous post I looked at my first attempt at creating a computational expression in F#. By anyone’s level this was basic. So I figured it was time to look at all the available “standard” methods available for a computational expression. By “standard” I means those which are implciitly recognized by F# as opposed to creating custom methods.

See Computation Expressions (F#) for more info.

There are plenty of posts on this subject from Microsoft or F# for fun or profit, so why am I writing yet another post – basically just to give my perspective and to help me remember this stuff.

Let’s start with the basics, we have a type SampleBuilder defined as

type SampleBuilder() =
   // our code will be filled is as required 
   // but an example might have just the following
   member this.Bind(m, f) = f m

We shall use the builder thus

let builder = SampleBuilder()

builder {
   // expressions, such as 
   let x = 100
}

and now to the various builder methods…

Bind

let! x = 100

// translates to

builder.Bind(100, (fun x -> expression))

So as can be seen, in essence the order of the arguments is reversed. So if we were to simply duplicate the way let binding works but in a computational expression we’d write something like

member this.Bind(m, f) = 
   f m

Return and ReturnFrom

At the time of writing this, I’m a little confused as to the difference between Return and ReturnFrom. Computation expressions and wrapper types states that the Return returns a wrapped type whilst ReturnFrom returns an unwrapped type. However it is seems it’s possible to pretty much return whatever I like as shown below

member this.Return(x) = x

member this.ReturnFrom(x) = x

Return is called when we have a return in the computation expression, i.e.

builder {
   return 100
} |> printfn "%A"

and ReturnFrom is used when return! exists, i.e.

builder {
   return! 100
} |> printfn "%A"

One slightly odd thing if you’re used to C# or the likes is that we can have multiple return and/or return! in a computational expression as per

builder {
   return 100
   return! 100
} |> printfn "%A"

As per multiple yield statements, we need a Combine method implemented in our builder to combine the return values. A return does not mean the rest of the computational expression is not executed, it means that the return values are combined to produce a value at the end of the workflow. So for example

builder {
   printfn "First"
   return 100
   printfn "Second"
   return 100
} |> printfn "%A"

Assuming our Return method and Combine are as simple as the following

member this.Return(x) = 
   printfn "Return"
   x

member this.Combine(a, b) = a + b

Then our output will look like the following

First
Return
Second
Return
200

In other words, the first printfn (actually the Delay builder method is called for each printfn) is called then the Return method, then the second printfn then the second Return, then the Combine is called. In this case Combine simply adds the return values together to produce the output of the workflow.

Combine

The combine method combines multiple values – so for example if you had something like the following

builder {
   yield 100
   yield 200
} |> printfn "%A"

without a combine method you’ll get the following compiler error This control construct may only be used if the computation expression builder defines a ‘Combine’ method. Simple enough to see the problem here. So we cannot yield more than once unless we implement a combine method. Here’s an example which implements the combine by creating a list and combining elements into a new list

member this.Combine(a, b) = a @ b

If we now compile this code we’ll get another error, “This control construct may only be used if the computation expression builder defines a ‘Delay’ method”. So we need to implement a Delay method. See the example listed for Delay below…

Delay

The Delay method allows the builder to delay the evaluation of a computational expression until it’s required. For example if we yield multiple times within a computational expression the idea is to combine the yields until we’re ready to invoke something.

The example above creates a list of the yields and then in the Delay we might want to do something with those combined yields. In this example below we’ll simply use the following code

member this.Delay(f) = f()

which essentially just invokes the function passed to the delay.

Run

Whilst the Delay method is used to delay evaluation, the Run method is mean’t to use the delayed expression, i.e. it might be to run something or the likes. An example which simply turns the list created by the combine into an array instead

member this.Run(f) = Array.ofList f

this is obviously a rather simplistic example, had our builder been creating the data for a web service call, for example, possibly run would make the call for us.

For

I again must point anybody reading this section to the excellent post Implementing a builder: The rest of the standard methods where an implementation of the For method is describe, for simplicity I’ll recreate it here

member this.For(sequence:seq<_>, body) =
   this.Using(sequence.GetEnumerator(),fun enum -> 
      this.While(enum.MoveNext, 
         this.Delay(fun () -> body enum.Current)))

The Using, While and Delay methods are described elsewhere in this post.

Here’s some sample code using the For method from our computational expression block

builder {        
   for i = 1 to 10 do
      printfn "%d" i
} 

TryFinally

An implementation of the TryFinally method is fairly straightforward

member this.TryFinally(body, compensation) =
   try this.ReturnFrom(body())
   finally compensation() 

and in usage within a computational expression block, we have

builder {        
   try
      let x = 1 / 0
      printfn "should not make it to here"
   finally
      printfn "exception"
} 

In the above code, when we step into the try block we will actually enter the TryFinally method and obviously this then takes over executing the body of the code and then executing the finally block.

TryWith

Very much like the TryFinally block, we have the following implementation

member this.TryWith(body, handler) =
   try this.ReturnFrom(body())
   with e -> handler e

and an example of the computational expression block might be

builder {        
   try
      let x = 1 / 0
      printfn "should not make it to here"
   with
   | :? DivideByZeroException -> printfn "divide by zero"
} 

Using

As the name suggests, this is used for IDisposable types. Here’s a sample of it’s implementation

member thisTryFinally(body, compensation) =
   try __.ReturnFrom(body())
   finally compensation() 

member this.Using(disposable:#System.IDisposable, body) =
   let body' = fun () -> body disposable
   this.TryFinally(body', fun () -> 
      match disposable with 
      | null -> () 
      | disp -> disp.Dispose())

The Using method relates to the use! keyword and hence we might have code such as

builder {        
   use! x = new MemoryStream()
   printfn "do something with the memory stream"
} 

So at the point use! is invoked our Using method takes over. We i

While

The example of a while loop in a computational expression was lifted from “Implementing a builder: The rest of the standard methods” and I highly recommend anyone visiting this post goes and reads the computational expression series on F# for fun and profit.

So our example code is as follows

let mutable i = 0
let test() = i < 5
let inc() = i <- i + 1

let builder = SampleBuilder()

builder {        
   while test() do
      printfn "%i" i
      inc()         
} |> ignore

It’s important to note that the three let statements at the top of the source must be global or you’ll get an error along the lines of “The mutable variable ‘i’ is used in an invalid way. Mutable variables cannot be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via ‘ref’ and ‘!’.”

So our builder code should look something like the following

member this.Bind(m, f) = f m

member this.Return(x) = x

member this.Zero() = this.Return ()

member this.Delay(f) = f

member this.Run(f) = f()

member this.While(guard, body) =
   if not (guard()) 
   then 
      this.Zero() 
   else
      this.Bind( body(), fun () -> 
         this.While(guard, body))  

So as can be seen, in the While method we are passed the guard and the body of the while loop and as expected we need to invoke these bits of code to evaluate them. When the guard completes we invoke the Zero method which in this case simply called the Return method. If the loop has not completed we simply keep looping.

Yield and YieldFrom

We can use yield or yield! from a computational expression, but to do so we need to implement the Yield and YieldFrom methods (respectively) on the builder, otherwise we’ll be greeted with the “This control construct may only be used if the computation expression builder defines a ‘Yield’ method”, here’s an example of the yield being used from a computational expression block

builder {
   yield 100
} |> printfn "%A"

// or

builder {
   yield! 100
} |> printfn "%A"

An example of the Yield and YieldFrom methods in the builder might look like the following

member this.Yield(x) = Some x

member this.YieldFrom(x) = x

Zero

You may have noticed, if you’ve written your own builder type that something like the following

builder {
}

// or

builder {
} |> ignore

doesn’t compile, this is because a computational expression must have something within the curly braces. So if instead we write the following

builder {
   printfn "Hello"
}

We’re get an altogether different error at compile time stating This control construct may only be used if the computational expression builder defines a ‘Zero’ method. Basically a computational expression must return something, either explicitly or implicitly. In this case Zero is used for any implicit return, so we could resolve this error using

member this.Zero() = ()

or ofcourse we might implicitly return the same thing we would with an explicit return such as

member this.Return(x) = x

member this.Zero() = this.Return ()

The with expression in C# 9.0 and later

I’ve not had cause to use this so far. Which seems strange (in a way) as I used similar language features with the spread operator and changing values in TypeScript a lot.

The with expression basically copies a record, struct or anonymous type and allows us to change some values, hence creating a new instance but overriding certain properties or fields.

Note: In C# 9.0 the with expression only worked with record types, in C# 10 it can be used, additionally, with struct and anonymous types.

Let’s look at a simple example. We have my old favourite demo type, a Person record which looks like this

public record Person(string Name, int Age);

Now we’ll create a Person like this

Person scooby = new("Scooby Doo", 7);

Console.WriteLine(scooby);

This would ofcouse output something like Person { Name = Scooby Doo, Age = 7 }.

Note: Obviously this is a rather simplistic record but it should give you the idea how things work. For more complex cases imagine there’s a first name, last name, lines of an address, national insurance/social security number etc. just to make it a little more work to create copies of a Person record instance.

Now let’s say we wanted to create a new record which has all the same properties/fields bar some properties/fields that will be changed. Ofcourse we could just create a new Person in the same way as above and change the properties. However, the with expression allows us to copy and change in a more elegant way (well more elegant if we had lots more properties/fields on the record than this example).

We can write

Person scooby = new("Scooby Doo", 7);
Person scrappy = scooby with { Name = "Scrappy Doo" };

Console.WriteLine(scooby);
Console.WriteLine(scrappy);

The scooby instance remains unchanged as we’ve essentially created a new instance of the Person record, copying the Age and explicitly changing the Name.

Now, what if we wanted to simply create a new instance of a Person with the same properties and fields as the scooby instance? We can just use the { } syntax like this

Person scoobyCopy = scooby with { };

Assert.IsFalse(Object.ReferenceEquals(scooby, scoobyCopy));

As the Assert states, scooby and scoobyCopy instances are not the same instance, they are copies.

Note: If using a struct (or if you’re explicitly supplying properties for a record) as the left-hand of the with expression and change values within the curly braces, you will need properties to have setters.

CommunityToolkit.Mvvm “already contains a definition for” error

The CommunuityToolkit.Mvvm includes a wonderful MVVM source generator, but in the current .NET 6.0 release I’m using, (doesn’t happen with MAUI but does with WPF) I get errors such as

  • The type ‘MyViewModel’ already contains a definition for ‘MyProperty’
  • A partial method may not have multiple defining declarations

To fix this (at least in the current version of .NET 6.x) add a global.json file to the folder with your solution file

{
  "sdk": {
    "version": "6.0.202",
    "rollForward": "disable"
  }
}

and things should work correctly – hopefully this post will be obsolete soon with the issue fixed, but for now this solves the problem.

WPF UserControl DependencyProperty Binding

On the weekend I was messing around creating a simple WPF card game application and wanted to just quickly knock together a UserControl with a dependency property (actually there were several controls and several dependency properties) which allows the hosting Window or UserControl to change the property.

Ofcourse we should probably look at implementing this as a lookless control but I was just trying to do the bare minimum to get this working as a proof of concept.

So what I want is UserControl which displays playing card, so let’s call it CardView and it looks like this (excluding the UserControl element for brevity)

<Grid>
   <Border Background="White" Padding="8" CornerRadius="8">
      <Image Source="{Binding CardImage, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
      <Border.Effect>
         <DropShadowEffect />
      </Border.Effect>
    </Border>
</Grid>

and the code behind looks like this

public partial class CardView : UserControl
{
   public static readonly DependencyProperty CardImageProperty = DependencyProperty.Register(
      nameof(CardImage), typeof(Uri), typeof(CardView), new PropertyMetadata(default(Uri)));

   public Uri CardImage
   {
      get => (Uri)GetValue(CardImageProperty);
      set => SetValue(CardImageProperty, value);
   }

   public CardView()
   {
      InitializeComponent();
   }
}

The key thing here is that we have this code-behind dependency property CardImage, and in the XAML we want to show the image of the card that was set through the dependency property.

If this was a lookless control we’d have set this binding up like this

RelativeSource={RelativeSource TemplatedParent}

but there’s no separation here of style and code, instead everything’s in the UserControl, so we have to use

RelativeSource={RelativeSource AncestorType=UserControl}

This will then bind the UserControl Image to the dependency property in code-behind and allow our host Window or UserControl to simply do the following

<local:CardView Width="140" Height="200" 
   CardImage="../Resources/HQ.png"/>

For this example, the png’s will be stored in a Resources folder with their Build Action set to Resource.

.NET HttpClient – the correct way to use it

For ages I’ve been using the HttpClient in .NET without any issues, but usually I have a single endpoint and a singleton (or equivalent) of a class that uses the HttpClient. A while back I needed to call multiple different endpoints using the HttpClient and so I happily wrapped an instantiation of an HttpClient in a using statement, I mean after all it has an IDisposable interface so that makes sense right?

Well NO this is not the case. So don’t do this!

// Don't do this
using(var httpClient = new HttpClient())
{
   // do something with httpClient
}

This post stems from a work colleague pointing me to the post YOU’RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE.

What does the documentation say?

If you take a look at the HttpClient Class documentation states

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors.

If we take a look at some C# templates in Visual Studio, for example the ASP.NET core Blazor application template, it will give you a scoped instance of the HttpClient, for example

builder.Services.AddScoped(sp => 
  new HttpClient 
    { 
       BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) 
    }
);

So how are we supposed to use a single instance of HttpClient

Let’s now assume that we create an HttpClient per application. Then how do we call multiple service endpoints without having changes affect different endpoint calls and is it threadsafe?

Is the HttpClient thread-safe? Well the answer is both yes and no.

Changing properties on the HttpClient are not threadsafe or at least cannot/should not be modified whilst there are outstanding requests. However methods such as GetAsync, PostAsync, SendAsync etc. are threadsafe which then leads us to how to se set-up different calls, i.e. maybe different headers etc. on all our calls. The answer here is to use SendAsync and create an HttpRequestMessage. This allows us to specify many of the properties we’ll need per HTTP call.

References

YOU’RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE
HTTPCLIENT CREATION AND DISPOSAL INTERNALS: SHOULD I DISPOSE OF HTTPCLIENT?

Nullable reference types in C#

Nullable reference types are now enabled by default on projects created with Visual Studio 2022, so let’s have a quick look at them…

Enabling nullable reference type checking

By default nullable reference types were disabled prior to Visual Studio 2022, so would need enable them at a project or file level. Enabling in the project means adding the following to the .csproj file

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net5.0</TargetFramework>
  <Nullable>enable</Nullable>
</PropertyGroup>

If you prefer to enable at a file level then simply add the following to the top of each file

#nullable enable

Now what?

So once you’ve enabled nullable reference type checking, especially if on a legacy code base, be prepared for warnings such as

  • warning CS8625: Cannot convert null literal to non-nullable reference type.
  • warning CS8604: Possible null reference argument for parameter ‘s’ in ‘void Program.SetName(string s)’.
  • warning CS8602: Dereference of a possibly null reference.

I’m sure there are plenty of other warnings, but you get the idea.

Basically what we’re ask the compiler to do is tell us when we might have the potential to be passing a null into a function etc. and highlight the potential issue with a warning. In other words we now need to mark reference types as nullable so the compiler knows we’re expecting a null and in situations where we’re not using a nullable reference type we’ll be warned.

Let’s see some example code. First off, if you default arguments to null, for example

public void SetName(string name = null)
{
   // do something
}

This will result in the warning >warning CS8625: Cannot convert null literal to non-nullable reference type.. All we need to do is tell the compiler we’re expecting a null, by making the argument nullable, i.e. add the ? to the type just like nullable value types.

public void SetName(string? name = null)
{
   // do something
}

Any usage of the variable name will now expect a null check, so if we compile the following

public void SetName(string? name = null)
{
   Console.WriteLine(name.Length);
}

as you’d imagine, the compiler will issue a warning here, in this case warning CS8602: Dereference of a possibly null reference., so obviously we have the potential of a null reference exception here, so adding a conditional check like this will fix that

static void SetName(string? name = null)
{
   if (name != null)
   {
      Console.WriteLine(name.Length);
   }
}

In some situations you may in fact know a variable/argument is not null – in TypeScript it’s not unusual to find the transpiler getting a little confused in which case we use the null-forgiving operator simply an !, so whilst the SetName method, as it stands ofcourse can be null, what if we remove the optional argument and expect all callers of the SetName method – for example maybe it’s a private method and we know each caller must check for null first anyway, then we use the following

static void SetName(string? name)
{
   Console.WriteLine(name!.Length);
}

We’re basically saying, by using name!. we know the value is not null. This example is a little contrived because we could just change the string? to string and not require the !, but you get the idea.

Struct, Class and now Record types in C#

I thought it’d be interesting to compare the three C# types, struct, class and record. I’m sure we all know that a struct is a value type and a class a reference type, but what’s a record.

The record “keyword defines a reference type that has built-in functionality for encapsulating data” (see Records (C# reference)).

Before we get into record. let’s review what struct and classes give us using the example of a Person type which has a FirstName property.

struct

struct Person
{
  public string FirstName { get; set; }
}
  • A struct extends System.ValueType and structs are “allocated either on the stack or inline in containing types and deallocated when the stack unwinds or when their containing type gets deallocated. Therefore, allocations and deallocations of value types are in general cheaper than allocations and deallocations of reference types.” See Choosing Between Class and Struct.
  • ToString() will output YouNameSpace.Person by default.
  • Structs cannot be derived from anything else (although they can implement interfaces).
  • Structs should be used instead of classes where instances are small and short-lived or are commonly embedded in other objects.
  • As structs are ValueTypes they are boxed or cast to a reference type or one of the interfaces then implement.
  • As a function of being a ValueType, structs are passed by value

class

class Person
{
  public string FirstName { get; set; }
}
  • A class extends System.Object and classes are reference types and allocated on the heap.
  • ToString() will output YouNameSpace.Person by default.
  • Obviously classes may extend other class but cannot extend a record and vice versa, records cannot extend classes.
  • Reference types are passed by reference.

record

Records can have properties supplied via the constructor and the compiler turns these into readonly properties. The syntax is similar to TypeScript in that we can declare the properties in a terse syntax, such as

record Person(string FirstName);

Whilst records are primarily aimed at being immutable we can still declare them with mutability, i.e.

class Person
{
  public string FirstName { get; set; }
}
  • Records can only inherit for other records, for example
    record Scooby() : PersonRecord("Scooby");
    
  • Records use value comparison (not reference comparison) via IEquatable
  • ToString formats output to show the property values
  • Records support the with keyword which allows us to create a copy of a record and mutate at the point of copying. Obviously as records are generally aimed at being immutable this use of with allows us to copy an existing record with some changes, for example
    var p = somePerson with { FirstName = "Scrappy" };
    

    This is not a great example with our record which has a single property, but if we assume Person also had a LastName property set to “Doo” then we’d essentially have created a new record with the same LastName but now with the new FirstName of “Scrappy”.

    We can also copy whole records by not supplying any values within the { } i.e.

    var p = somePerson with { };
    

More Pattern Matching in C#

A while back I wrote a post C# 8.0 enhancements with pattern matching which was very light on the subject. Let’s look a little more in depth at options for pattern matching.

is/as in pattern matching syntax

Often (if you’ve been writing C# for a while) you’ll used to writing code like this

var person = o as Person;
if (person != null)
{
   Console.WriteLine(person.FirstName);
}

i.e. we have an object o which we don’t know the type of, so we use as to convert this to a Person type or null if it’s not of that type.

This can be replaced with a slightly more concise syntax (and what’s known as the declaration pattern).

if (o is Person person)
{
  Console.WriteLine(person.FirstName);
}

Null checks

This is a pattern known as a constant pattern

if (o is null)
{
  Console.WriteLine("Is null");
}

Along with a logical pattern using not we can also write

if (o is not null)
{
  Console.WriteLine("Is not null");
}

We can also use this pattern with types, for example

if (o is not (string or null))
{
  Console.WriteLine("Is NOT string or null");
}

<strong>Pattern matching when values match a criteria</strong>

If we extend the above Pattern Matching to not just check the type is a Person but also that the Age property is greater than 4, so we can now replace

[code language="csharp"]
if (o is Person p && p.Age > 4)
{
  Console.WriteLine($"Older than 4 {p.FirstName}");
}

with the following

if (o is Person { Age: > 4 } p)
{
   Console.WriteLine($"Older than 4 {p.FirstName}");
}

In the above we’re using a property pattern.

Switch patterns matching

Exhaustive switches can be used to match types using switch statements, for example

var result = o switch
{
  string s => $"String {s}",
  Person p => $"Person {p.FirstName}",
  _ => throw new ArgumentException("Unhandled type")
};

Note the use of the _ (discard) ensuring this is an exhaustive switch.

Better still we can also use other features of pattern matching in the switch like this

var result = o switch
{
  string s => $"String {s}",
  Person { Age: > 4} p => $"Person {p.FirstName} > 4",
  Person p => $"Person {p.FirstName}",
  null => "In Null",
  _ => throw new ArgumentException("Unhandled type")
};

In the above we’re switching based upon type and also matching values.

We can also use relational patterns, in this example we’ll assume o is an int

var result = o switch
{
  1 or 2 => "One or Two",
  > 2 and < 4 => "Mid",
  >= 4 and < 6 => "High",
  6 => "Six",
  _ => "Other"
};

Before we leave the switch statement we can also match against types using the “standard” switch syntax, i.e.

switch (o)
{
  case string s:
    Console.WriteLine(s);
    break;
  case Person p:
    Console.WriteLine(p.FirstName);
    break;
}

Tuples

Pattern matching also allows us to match against tuples and use the discard to ignore parts we’re not interested in, for example

var result = o switch
{
  (1, _) => "First One",
  (_, 0) => "Second Zero",
  _ => "Other"
};

Creating new variables from patterns

We can also use the var pattern to assign values from the patterns

var result = o switch
{
  var (a, b) when a < b => new Tuple<string, int, int>("First less than second", a, b),
  var (a, b) when a > b => new Tuple<string, int, int>("Second greater than first", a, b),
  var (a, b) => new Tuple<string, int, int>("Equals", a, b)
};

In this example we’re deconstructing a tuple and assigning to some new value (in this case an extended Tuple, just because I couldn’t think of a better example – check the documentation link above for a better example).