Skip to content

Instantly share code, notes, and snippets.

@divega
Last active May 21, 2022 01:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save divega/aa23a0f22f6dca3654677d37590298b9 to your computer and use it in GitHub Desktop.
Save divega/aa23a0f22f6dca3654677d37590298b9 to your computer and use it in GitHub Desktop.
Playing with recent and upcoming features from C# 8.0 through 11.0
// What's new in C#
// Good places to start:
// - https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-version-history#c-version-72
// - https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-11
#region implicit and global usings (C# 10)
global using Microsoft.EntityFrameworkCore;
global using System.Collections;
using System;
#endregion
#region top-level statements (C# 10)
Console.WriteLine("Waiting to start...");
// await Task.Delay(TimeSpan.FromMinutes(5));
Console.WriteLine("Hello team!");
#endregion
#region raw strings (C# 11)
// This is a raw string, new in C# 11 preview. No more pain pasting your JSON into code!
Console.WriteLine("""
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}
""");
// What about interpolated strings?
Console.WriteLine($$$"""
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "{{{"S"}}}",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}
""");
#endregion
#region interpolated string improvements
// See https://sharplab.io/#v2:EYLgtghglgdgNAExAagD4AEBMBGAsAKAPQGYACLc7AdgIG8DTHKA2cgFlIFloYAKASlIMmtIfiYTKABlLBSAXlIAiAB5KA3MMnTSAZwAuAJwWkAJEoi6EAM1rAAvhq2T02AJy8Dh/pvHax/qQAxgD2MAY6QSaqTn7aoeH6kWFeJuaWNrRBjr6Brh4JXj4EzvYE9kA===
#endregion
#region switch expressions (C# 8), caller argument expression (C# 10), list matching (C# 11)
var list = new[] { 1, 2, 3, 4 };
Describe(list);
Describe(new[] { 1, 2, 3 });
Describe(Array.Empty<int>());
Describe("I am not a list");
static void Describe<T>(T parameter, [System.Runtime.CompilerServices.CallerArgumentExpression("parameter")] string? name = default) =>
Console.WriteLine($"""
The expression "{name}" represents { // multi-line expressions and comments in the interpolated string "holes" (C# 11)
parameter switch
{
IList and [var a, var b, _] => $"a list of 3 elements, {a}, {b}, and some other element we don't care about",
IList and [var a, ..] => $"a non-empty list that starts with {a}",
IList and { Count: 0 } => "an empty list",
_ => "something that isn't a list"
}}.
""");
#endregion
#region async streams (using EF Core) (C# 8), nullable reference types (C# 8), init-only settters (C# 9)
{ // Let's persist some data.
using var context = new DatabaseContext(); // using declaration (gets disposed automatically at the end of the of scope)
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
context.Add(
new Person
{
Id = 1,
PartitionKey = "1",
FirstName = "John",
LastName = "Doe"
});
context.Add(
new Person
{
Id = 2,
PartitionKey = "2",
FirstName = "Jane",
LastName = "Doe"
});
await context.SaveChangesAsync();
}
{ // Let's query some data.
using var context = new DatabaseContext(); // using declaration (gets disposed automatically at then end of scope)
// To see how foreach works, see https://sharplab.io/#v2:EYLgtghglgdgNAExAagD4AEBMBGAsAKHQAYACdbAOgBlYBHAbgIPQGYzMzsB2EgbyfwkhnAGxkALCQCy0GAAoAlHwLDVJAG4QATiQBmEANYBTAIoBXI1oCeJALwkYRgO4kA2gF0+AIghe4JL2A/AIBjLwBfCgBBAGdzSysIYAAbI0VGQTUhXQB7LSMIEIALOU0dIxJYPUNTC2sFFSzlTKbhAGEcmBic1IoAdS0oABc0owUM1vDGoSn8cKA==
await foreach (var person in context.People.Where(p => p.LastName == "Doe").AsAsyncEnumerable().WithCancellation(default))
{
Console.WriteLine($"Here is a person: {person.FirstName} {person.LastName}");
}
Console.WriteLine();
}
public class Person
{
public int Id { get; set; }
// ! is the new "null-forgiving operator".
// Assigning default! signals the compiler we don't need to be warned that the non-nullable property isn't initialized.
public string PartitionKey { get; set; } = default!;
public string? FirstName { get; set; }
public string LastName { get; set; } = default!;
}
public class DatabaseContext : DbContext
{
public DbSet<Person> People { get; init; } = default!; // Initialization-only settters
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseCosmos(
"AccountEndpoint = https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"MyDatabase");
}
}
#endregion
#region generic attributes (C# 11)
public class DefaultDatabaseValue<T> : Attribute
{
public DefaultDatabaseValue(T value)
{
Value = value;
}
public T Value { get; }
}
public class PersonTest
{
[DefaultDatabaseValue<int>(0)]
public int Id { get; set; }
[DefaultDatabaseValue<string>("J. Doe")]
public string? Name { get; set; }
}
#endregion
#region readonly struct members (C#8)
public struct Point
{
public double X { get; set; }
public double Y { get; set; }
public readonly Measure Z { get; }
public readonly double Distance => Math.Sqrt(X * X + Y * Y);
public readonly override string ToString() => $"({X}, {Y}) is {Distance} from the origin";
}
public struct Measure
{
public readonly double Member { get; }
}
#endregion
#region records (C# 9)
// See https://sharplab.io/#v2:EYLgtghglgdgNAExAagD4AEBMBGAsAKAPQGYACLc7AdlIG8CDSnKA2cgFlIFloYAKAJR1GzUQAUApgCcAzgHsYAJQkBjOVISkADtPkxSAXlIwJAdz4AiAFIA6C3AsARORIsCA3CNFMv3gG4QUqQArloIEAAuEgiSsgqG2rrxplARABZ0pABiULIRAHIQYBIJ1hAmFqQAvp743sy+oujYAJx8jfWkACQWACppJaHhUZo6cTAA5DKkAGa5MhHGRSVQ0/R1naJDkdGxejY5eYXFVW61HT4b9c1tPRYWF960Y3pVpKvCV5vefC8KAIQGbYjPYKAD8pAsMDkiwsIEhbkeoiqEgAjsEIAAbOjA3ZJGBVJHMe5nXy+Qn4ClEMhSVTqTSgpR0jR8ZoABmy8wKyzglA5ABkIAtjhIPETvp1qaRaWoNKQAKJgLSYuQATwkJVZ2A5h2FPL5pEFeuKvPZCqVKvVUgE8N8jOUsoQfF13JNhqFrtFtSAA=
#endregion
#region UTF8 strings
// See https://sharplab.io/#v2:EYLgtghglgdgNAExAagD4AEBMBGAsAKAPQGYACLc7AdlIG8CDSnKA2cgFlIFloYAKAJR1GzUcACeAFwCmAbQC6pAK6SAZgA4AypIBOsAOakAvKQBEACWkAbKwHtSAVQAqAMXWltemPoDOAQlMldQBuEVEmdGwATj4VDU8DAVD8UQBfMNICVKA===
#endregion
#region default interface methods (C# 8)
// See https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-8#default-interface-methods
#endregion
#region indices and ranges (C#8)
// See https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-8#indices-and-ranges
#endregion
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>11</LangVersion>
<EnablePreviewFeatures>True</EnablePreviewFeatures>
</PropertyGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="7.0.0-preview*" />
</ItemGroup>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment