.NET 6.0 and C# 10 are just around the corner, so now is a good time to review some of the most interesting new language features!
Record structs đź“„ Proposal
Records were introduced in C# 9 as a simple way to define data types with value equality semantics, for instance:
public record Money(decimal Amount, string CurrencyCode); An annoying limitation was that records were always reference types, but in some scenarios it would have been better to use value types.
Dependency injection: the good and the bad Dependency injection (DI) is a great pattern, which can really help make your code cleaner, more decoupled and more testable. There are many DI libraries, like Autofac, Lamar (StructureMap’s successor), Castle Windsor, etc., but lately I’ve mostly been using the one provided by Microsoft in .NET Core : Microsoft.Extensions.DependencyInjection. It’s not the most full-featured (in fact, it’s pretty bare-bones), but I find it sufficient in most cases.
Last week at Microsoft Build, there have been a lot of exciting annoucements! .NET 5, Blazor WebAssembly, .NET MAUI, WinUI… But the thing I’m most eager to get my hands on is C# 9, which introduces many interesting new features, so let’s take a quick tour! There’s a long list, so I won’t cover all of them here, but I will highlight the ones I find the most interesting.
Note: Unfortunately the new C# features aren’t supported yet in the latest SDK preview, so we can’t test them in actual projects.
As a C# developer, there are obviously a lot of skills you need to master to be effective: language syntax, framework classes, third-party libraries, databases, regular expressions, the HTTP protocol, etc. But there are a handful of things that I consider to be really fundamental, and I often see C# developers, even experienced ones, who don’t master them. So, I’m doing a series about those things! Today: hash codes.
The GetHashCode method OK, I realize that most developers don’t need to implement their own hash table, or even implement GetHashCode very often, but still, it’s important to know about this.
Over the last few years, OpenID Connect has become one of the most common ways to authenticate users in a web application. But if you want to use it in a desktop application, it can be a little awkward…
Authorization code flow OpenID Connect is an authentication layer built on top of OAuth 2.0, which means that you have to use one of the OAuth 2.0 authorization flows. A few years ago, there were basically two possible flows that you could use in a desktop client application to authenticate a user:
The problem with circular dependencies When building an application, good design dictates that you should avoid circular dependencies between your services. A circular dependency is when some components depend on each other, directly or indirectly, e.g. A depends on B which depends on C which depends on A:
It is generally agreed that this should be avoided; I won’t go into the details of the conceptual and theoretical reasons, because there are plenty of resources about it on the web.
Query strings are typically made of a sequence of key-value pairs, like ?foo=hello&bar=world…. However, if you look at RFC 3986, you can see that query strings are very loosely specified. It mentions that
query components are often used to carry identifying information in the form of “key=value” pairs
But it’s just an observation, not a rule (RFCs usually have very specific wording for rules, with words like MUST, SHOULD, etc.). So basically, a query string can be almost anything, it’s not standardized.
Out of the box, ASP.NET Core has the concept of “environments”, which allows your app to use different settings based on which environment it’s running in. For instance, you can have Development/Staging/Production environments, each with its own settings file, and a common settings file shared by all environments:
appsettings.json: global settings appsettings.Development.json: settings specific to the Development environment appsettings.Staging.json: settings specific to the Staging environment appsettings.Production.json: settings specific to the Production environment With the default configuration, environment-specific settings just override global settings, so you don’t have to specify unchanged settings in every environment if they’re already specified in the global settings file.
A few years ago, I blogged about a way to automate unit testing of null argument validation. Its usage looked like this:
[Fact] public void FullOuterJoin_Throws_If_Argument_Is_Null() { var left = Enumerable.Empty<int>(); var right = Enumerable.Empty<int>(); TestHelper.AssertThrowsWhenArgumentNull( () => left.FullOuterJoin(right, x => x, y => y, (k, x, y) => 0, 0, 0, null), "left", "right", "leftKeySelector", "rightKeySelector", "resultSelector"); } Basically, for each of the specified parameters, the AssertThrowsWhenArgumentNull method rewrites the lambda expression by replacing the corresponding argument with null, compiles and executes it, and checks that it throws an ArgumentNullException with the appropriate parameter name.
Just a quick tip today!
for and foreach loops are among the most useful constructs in a C# developer’s toolbox. To iterate a collection, foreach is, in my opinion, more convenient than for in most cases. It works with all collection types, including those that are not indexable such as IEnumerable<T>, and doesn’t require to access the current element by its index.
But sometimes, you do need the index of the current item; this usually leads to one of these patterns: