C# 9 in Unity (2021.2 and up)
From Unity version 2021.2 upwards we can use C# 9 (some of its features). Here are some of the more useful ones.
Here is Enemy.cs example class that I will be using through some of the examples:
class Enemy { public int Power { get; set; } public int Health { get; set; } public bool IsDead { get; set; } public string Name { get; set; } = "Bob"; }
Target typing - no more class names in new()
First useful feature is that we do not have to type again the class name when creating in new one:
List<Enemy> enemies = new() { new() { Power = 3, Health = 1}, new() { Power = 4, Health = 2}, new() { Power = 5, Health = 3, Name = "John"}, new() { Power = 7, Health = 4, Name = "John"}, new() { Power = 8, Health = 5}, };
As you can see above we declare a new list of enemies without adding new List<Enemy>() {…} which is a lot of code that we can omit.
Earlier we also had to create an enemy by calling Enemey e = new Enemy(){….} (or using a constructor). Now we can omit the second “Enemy” declaration - unless you are using Inheritance and want to create some other subtype and the Enemy class is a supertype.
2. Pattern Matching - clearer condition statements
Pattern Matching is nothing more than checking some condition to see if an object is a match for it.
Previously we had to write something like this
for (int i = 0; i < enemies.Count; i++) { if (enemies[i].Health > 1 && enemies[i].Health < 4 && enemies[i].Power < 8) { Debug.Log($"Enemy with index {i} is a worthy opponent"); } }
Here to check the value contained in the Enemy object we have to access the enemy enemies[i] and next access the variable.
Now we can create a bit clearer statement using the updated Pattern Matching mechanics of C#9:
for (int i = 0; i < enemies.Count; i++) { if (enemies[i] is Enemy { Health: > 1 and < 4, Power: < 8 }) { Debug.Log($"Enemy with index {i} is a worthy opponent"); } }
First you can see that we declare once what we want to compare: enemies[i]
Next we use is instead of == to check equality - which for me at least reads more clearly and helps me avoid the error of only typing = once :)
Lastly we now can declare in a single place all the parameters that we want to check in this expresion: Enemy { Health: > 1 and < 4, Power: < 8 }
Here we can declare what are we looking for ex if Health is equal to 1: Health: 1 or if it is greater or lesser like in the example above.
We also use and instead of && and or instead of ||
UPDATE:
IS only works when comparing objects and const values. It will not work when comparing 2 variables (if those are not objects).
Here you can learn more about this topic docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/patterns3
Are you enjoying this article so far?
Do you want to learn more about coding in Unity? Check out my OOP for Unity Devs video course and others !
3. Records - new type (partially implemented in Unity)
Records are a new reference type that allows us to easily encapsulate data (bundle data that is connected in some way together to create more OOP code). The benefit of it is that it generates a lot of code for us (to compare, get and assign data to a record).
Unfortunately Unity does not support .NET 5.0 - record types requires Init (another cool feature that allows us to initialize a property only in the constructor) but there is a work around:
Users can work around this issue by declaring the System.Runtime.CompilerServices.IsExternalInit
type in their own projects. (source)
Here is how we create a record type called Person:
namespace System.Runtime.CompilerServices { public class IsExternalInit { } } public record Person(string Name, int index);
Person record type contains a string name and int index.
The cool thing about those is that we have written hardly any code but here is the functionality that we get by default:
Person person = new("Peter", 1); Person person1 = new("Peter", 1); Person person2 = new("John", 3); if (person == person1) { Debug.Log("Mathing name and index!"); } ///Mathing name and index! var (personsName, personsIndex) = person2; Debug.Log($"name: {personsName}, index: {personsIndex}"); ///name: John, index: 3
We can compare Person record objects and by default both name and index values are compared - again we did not add this comparison. It just works!
We also see that we can very easily get all the data saved in a record type since every record created by default a Deconstructor that allows us to grab all the parameters contained in that record. Here we create an annonymous tuple (I believer): var (personsName, personsIndex).
And that is mostly it! You can read more about C# 9 features here docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9
I hope that you have enjoyed this blog post!
If you did be sure to check my Unity video courses at courses.sunnyvalleystudio.com/ if you want to learn more and support my work.
You can also check my free Unity tutorials at youtube.com/c/SunnyValleyStudio
You can also support me through Patreon:
If you agree or disagree let me know by joining the Sunny Valley Studio discord channel :)
Thanks for reading!
Peter