I have completed viewing this talk titled “Change your habits: Modern techniques for modern C#” by Bill Wagner (TIL: Bill authored Effective C#). I list down below, all that I have learned from this talk. You can watch the full video at the end of this post (with privacy enabled).
1. Using Tuples in Constructor
Assume we have a Point struct:
public struct Point { public double X { get; private set; } public double Y { get; private set; } private double? distance; public double Distance { get { if (!distance.HasValue) distance = Math.Sqrt(X * X + Y * Y); return distance.Value; } } public Point(double x, double y) { X = x; Y = y; distance = default; // default only available in C# 7.1 } }
Instead of this constructor:
public Point(double x, double y) { X = x; Y = y; distance = default; // default is only available in C# 7.1 }
We use:
public Point(int x, int y) => (this.x, this.y, this.distance) = (x, y, default);
2. Tuples in Operator Overloading
We are still in the Point struct context. Instead of:
public static operator ==(Point left, Point right) => left.x == right.x && left.y == right.y; public static operator !=(Point left, Point right) => left.x != right.x || left.y != right.y;
Use
public static operator !=(Point left, Point right) => (left.x, left.y) != (right.x, right.y);
3. Using Tuples to Swap Values
Remember that interview question where you supposed to swap two values without introducing a third variable? Tuples can easily help you do that. Bill’s sample below is to swap the X,Y coordinates of Point struct.
public void SwapCoords() => (X,Y) = (Y,X);
Based on above example, I created below to help me understand Tuples more.
static void Main(string[] args) { var originalTuple = (10, 20); var swappedTuple = SwapValue(originalTuple); Console.WriteLine("Original:" + originalTuple); // Original:(10, 20) Console.WriteLine("Swapped:" + swappedTuple); // Swapped:(20, 10) Console.ReadLine(); } public static (int, int) SwapValue((int, int) t) => (t.Item2, t.Item1);
4. Immutable Struct Using Readonly
Below is the completely immutable Point struct
public readonly struct Point { public double X { get; } public double Y { get; } public double Distance { get; } public Point(double x, double y) => (X, Y, Distance) = (x, y, Math.Sqrt(x * x + y * y)); }
5. Throw ArgumentNullException on null parameter
Below is a Person class which will throw ArgumentNullException upon null parameter. The example also using new keyword nameof, which was introduced in C# 6.0.
public class Person { private string _firstName; private string _lastName; public string FirstName { get => _firstName; set => _firstName = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "cannot set name to null"); } public string LastName { get => _lastName; set => _lastName = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "cannot set name to null"); } public string HypenatedForPartner(Person partner) { // _ is a discard variable so we can do the null checking // without using "if" statement _ = partner ?? throw new ArgumentNullException(paramName: nameof(partner), message: "Partner should not be null"); return $"{partner.LastName} - {this.LastName}"; } }
6. Using Tuples in Switch Pattern Matching
Similar to what I have covered before, but now Bill have shown me that we can use Tuples in pattern-matching switch statement.
Take a look at PeakTimePremium method. Read and understand the business requirements in the comment above it. And now imagine how would you implement it using if-else statements. Then now compares it with the how it is actually implemented using pattern-matching switch statement. The pattern-matching switch statement is very much clearer and easier to understand!
public static class TollCalculations { private static bool IsWeekDay(DateTime timeOfToll) => timeOfToll.DayOfWeek switch { DayOfWeek.Saturday => false, DayOfWeek.Sunday => false, _ => true }; private enum TimeBand { MorningRush, DayTime, EveningRush, Overnight } private static TimeBand GetTimeBand(DateTime timeOfToll) { int hour = timeOfToll.Hour; if (hour < 6) return TimeBand.Overnight; else if (hour < 10) return TimeBand.MorningRush; else if (hour < 16) return TimeBand.DayTime; else if (hour < 20) return TimeBand.EveningRush; else return TimeBand.Overnight; } // Calculate a peak time multiplier: // weekend multiplier is 1.0 // late night / early morning is a discount, 0.75 // daytime during any weekday is 1.5 // morning rush inbound is double (2.0) // morning rush outbound is 1.0 // evening rush inbound is 1.0 // evening rush outbound is double (2.0) public static decimal PeakTimePremium(DateTime timeOfToll, bool inbound) => (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch { (true, TimeBand.MorningRush, true) => 2.0m, (true, TimeBand.EveningRush, false) => 2.0m, (true, TimeBand.DayTime, _) => 1.5m, (_, TimeBand.Overnight, _) => 0.75m, (_, _, _) => 1.0m }; }
After learning all these new features in C#, my only gripe is that it only works in Visual Studio 2019. Visual Studio 2017 is only supporting up to .NET Core 2.1.
That’s all guys. I hope it somehow helps you. Below is the full video of Bill’s talk: