The modern era of programming has become primarily defined by context-based programming models. What do I mean by “ context-based” programming models? Most applications we write will operate in an environment that we cannot fully recreate in our local development machines. For example, if you’re an AWS or Windows Azure developer, you likely utilize a combination of a software development kit (SDK), emulator, and remote calls. Another good example is chatbot SDKs, which provide a foundational set of methods and functionality present in a production environment but not locally.

Suppose you’re a software vendor building a Context-based programming model. In that case, you might be interested in using the latest features in .NET 6 to provide the lowest amount of friction to using your service.

In this post, we’ll explore .NET 6 features that can provide an improved programming experience for developers you hope to build for your new platform.

The Problem

Context-based programs have assumptions about the resources available to a running instance. Take a chatbot as an example. Given the domain problem, you’re likely to have some of the following environmental properties: incoming text, user, channel, storage, etc. These elements are essential to every interaction, and the program you are writing in this context wouldn’t function without them.

Instead of having developers go through the ritual of adding namespaces, registering handlers, and defining method parameters, wouldn’t it be easier to remove all of the ceremonies?

With .NET 6, we can do just that, using features like top-level statements, global using statements, and partial Program class.

The Solution

We want to provide a context for developers to discover features and functionality for our context-based programming environment. The first step is to use top-level statements to provide a clean-room-like state for programming. Top-level statements allow you to remove namespace definitions and the boilerplate around the Program class. Since ** .NET 6 RC1**, you can now access the Program type as a first-class concept, even if you don’t define it yourself.

// Program.cs
Console.WriteLine($"Hello, {nameof(Program)}");

The program works because of top-level statements, a referencable Program, and implicit global using statements.Looking into our Debug folder within the obj folder, we’ll find the *.GlobalUsings.cs file, which allows us to access the Console type.

// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

Ok, what about contextual properties and members? We have two options: Partial Program Class or Global Using Static class.

The first option of Partial Program Class requires a partial class Program within the same assembly as our top-level statements file.

// Program.Partial.cs
public partial class Program
{
    public static string Greeting
        => "Hello, World!";
}

Then, in our top-level statements file, we can access the new static property of Greeting.

// Program.cs
Console.WriteLine($"{Greeting}");

**Note: the members must be static, as the top-level statements file executes within the static context of static void Main. Instance members will be inaccessible. **

The other approach to getting context-based members into a top-level statements file is global using static.

global using static Globals;

public static class Globals
{
    public static string Name =>
        nameof(Globals);
}

In our Program.cs file, we can use the Name static member.

Console.WriteLine($"{Name}");

Great, but you may be asking. How useful are these features if we need to define them inside the same project that contains our top-level statements file? Well, that’s a great question, and technically you’d be right. You may remember a feature added in .NET 5 called source generators. Check out my jumpstart guide on Source Generators. if you’re not familiar with the .NET feature.

We can generate all of these techniques within the context of our application, and it will enable the discoverability of these context-based members. Great! Unlimited power!

Conclusion

If you’re building a context-based programming environment, like a chatbot or web functions framework, you may want to consider utilizing some of the newer features found in .NET 6. For example, you can provide users with a scripting-like experience while helping them discover functionality using standard IDE tooling. The added benefit of these approaches is that you can continue to upgrade and enhance APIs in the context of apps without the fear of introducing significant breaking changes.

If you end up using these techniques, please share where and how you used them. Also, remember to follow me on Twitter at @buhakmeh.