Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .github/workflows/build-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ on:
- "cloud-native/dockerize-aspnet-core-clean-images/**"
- "cloud-native/health-resilience-zero-downtime/**"
- "cloud-native/polly-resilience/**"
- "csharp-language/csharp-12-features/**"
- ".github/workflows/build-samples.yml"

pull_request:
Expand All @@ -40,6 +41,7 @@ on:
- "cloud-native/dockerize-aspnet-core-clean-images/**"
- "cloud-native/health-resilience-zero-downtime/**"
- "cloud-native/polly-resilience/**"
- "csharp-language/csharp-12-features/**"
- ".github/workflows/build-samples.yml"

workflow_dispatch:
Expand Down Expand Up @@ -693,3 +695,102 @@ jobs:
cloud-native/polly-resilience/PollyCatalogResilience.slnx
--configuration Release
--no-build

test-csharp-12-features:
name: Test C# 12 refactoring sample
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Install .NET 10 SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"

- name: Restore
run: >
dotnet restore
csharp-language/csharp-12-features/CSharp12RefactoringLab.slnx

- name: Build
run: >
dotnet build
csharp-language/csharp-12-features/CSharp12RefactoringLab.slnx
--configuration Release
--no-restore

- name: Test
run: >
dotnet test
csharp-language/csharp-12-features/CSharp12RefactoringLab.slnx
--configuration Release
--no-build

- name: Verify explicit C# language version
shell: bash
run: |
app_version="$(
dotnet msbuild \
csharp-language/csharp-12-features/src/CSharp12RefactoringLab/CSharp12RefactoringLab.csproj \
-getProperty:LangVersion
)"

test_version="$(
dotnet msbuild \
csharp-language/csharp-12-features/tests/CSharp12RefactoringLab.Tests/CSharp12RefactoringLab.Tests.csproj \
-getProperty:LangVersion
)"

echo "Application LangVersion: ${app_version}"
echo "Test LangVersion: ${test_version}"

test "${app_version}" = "12.0"
test "${test_version}" = "12.0"

- name: Run deterministic console sample
shell: bash
run: |
output="$(
dotnet run \
--project csharp-language/csharp-12-features/src/CSharp12RefactoringLab/CSharp12RefactoringLab.csproj \
--configuration Release \
--no-build
)"

printf '%s\n' "${output}"

expected="$(
printf '%s\n' \
"C# 12 Todo Refactoring Lab" \
"Counts: total=4, active=2, completed=2" \
"TODO: Welcome to the C# 12 lab [done]" \
"TODO: Adopt primary constructors [active]" \
"TODO: Use collection expressions [done]" \
"TODO: Try default lambda parameters [active]" \
"TODO: Review explicit properties [done]"
)"

if [[ "${output}" != "${expected}" ]]; then
echo "Console output did not match the expected output."

diff \
--unified \
<(printf '%s\n' "${expected}") \
<(printf '%s\n' "${output}") \
|| true

exit 1
fi

line_count="$(
printf '%s\n' "${output}" |
wc --lines |
tr --delete ' '
)"

test "${line_count}" = "7"
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Each sample folder contains a focused implementation of one tutorial topic. The
| [`cloud-native/dockerize-aspnet-core-clean-images`](cloud-native/dockerize-aspnet-core-clean-images/) | Focused .NET 10 container sample demonstrating a multi-stage Dockerfile, locked restore, runtime-only image, non-root execution, port 8080, runtime configuration, health checks, hardened Compose settings, and CI smoke testing | [Dockerizing ASP.NET Core: Multi-Stage Builds, Clean Images & a Production-Ready Ship Workflow](https://www.dotnet-guide.com/tutorials/cloud-native/dockerize-aspnet-core-clean-images/) |
| [`cloud-native/health-resilience-zero-downtime`](cloud-native/health-resilience-zero-downtime/) | Focused .NET 10 Orders API demonstrating tagged liveness/readiness checks, structured health output, shutdown readiness draining, typed HttpClient retries, circuit breaking, attempt timeouts, and deterministic integration testing | [.NET 8 Cloud-Native: Health Probes, Polly Resilience & Zero-Downtime Kubernetes Deployments](https://www.dotnet-guide.com/tutorials/cloud-native/health-resilience-zero-downtime/) |
| [`cloud-native/polly-resilience`](cloud-native/polly-resilience/) | Focused .NET 10 Polly v8 catalog sample demonstrating explicit stale-cache fallback, timeout-driven degradation, outbound concurrency isolation, strategy event counters, and deterministic integration testing | [Polly Resilience (Polly v8): Timeouts, Retries, Circuits, Bulkheads, Hedging](https://www.dotnet-guide.com/tutorials/cloud-native/polly-resilience/) |
| [`csharp-language/csharp-12-features`](csharp-language/csharp-12-features/) | Focused .NET 10 console lab locked to C# 12.0, demonstrating primary constructors with explicit properties, collection expressions and spreads, default lambda parameters, tuple-type aliases, deterministic output, and unit tests | [C# 12 Language Features: Primary Constructors, Collections & More](https://www.dotnet-guide.com/tutorials/csharp-language/csharp-12-features/) |

## Companion articles
- [Common Microsoft.Extensions.AI mistakes](https://www.dotnet-guide.com/articles/dotnet-ai/microsoft-extensions-ai-common-mistakes/)
Expand Down Expand Up @@ -267,6 +268,24 @@ tutorials/
| `-- PollyCatalogResilience.Tests/
| |-- PollyCatalogResilience.Tests.csproj
| `-- CatalogResilienceTests.cs
|-- csharp-language/
| `-- csharp-12-features/
| |-- CSharp12RefactoringLab.slnx
| |-- README.md
| |-- src/
| | `-- CSharp12RefactoringLab/
| | |-- CSharp12RefactoringLab.csproj
| | |-- Program.cs
| | |-- Formatting/
| | | `-- TodoFormatter.cs
| | |-- Models/
| | | `-- TodoItem.cs
| | `-- Services/
| | `-- TodoService.cs
| `-- tests/
| `-- CSharp12RefactoringLab.Tests/
| |-- CSharp12RefactoringLab.Tests.csproj
| `-- CSharp12FeatureTests.cs
|-- aspnet-core/
| |-- api-security-in-practice/
| | |-- ApiSecurityMinimal.slnx
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/CSharp12RefactoringLab/CSharp12RefactoringLab.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/CSharp12RefactoringLab.Tests/CSharp12RefactoringLab.Tests.csproj" />
</Folder>
</Solution>
202 changes: 202 additions & 0 deletions csharp-language/csharp-12-features/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# C# 12 Everyday Refactoring Lab

A focused .NET 10 console companion demonstrating practical C# 12 refactoring
with primary constructors, explicit public properties, collection expressions,
spread elements, default lambda parameters, and an alias for a tuple type.

## Full tutorial

[C# 12 Language Features: Primary Constructors, Collections & More](https://www.dotnet-guide.com/tutorials/csharp-language/csharp-12-features/)

## Framework and language note

The tutorial introduces C# 12 with the .NET 8 toolchain.

This companion targets .NET 10 because that is the DOTNET GUIDE repository's
current SDK, but both projects explicitly set:

```xml
<LangVersion>12.0</LangVersion>
```

The compiler accepts C# 12 syntax while rejecting newer C# 13 or C# 14 features
accidentally introduced during maintenance.

The project doesn't use `latest` or `preview`.

## What the sample demonstrates

- a class primary constructor;
- explicit properties initialized from constructor parameters;
- validation through a property initializer;
- a service primary constructor;
- collection expressions targeting `List<T>` and arrays;
- spread elements for copying and composition;
- a tuple alias using C# 12 "alias any type";
- a default lambda parameter;
- an explicit override of the lambda default;
- deterministic output;
- seven tests.

## Primary-constructor boundary

Primary-constructor parameters are parameters, not class members.

This class:

```csharp
public sealed class TodoItem(string title)
{
public string Title { get; } = title;
}
```

has a public `Title` property because the property is explicitly declared.

It doesn't automatically gain a public property named `title`.

The tests protect this distinction with reflection.

## Collection-expression boundary

Collection expressions are target-typed.

These compile because the target type is known:

```csharp
TodoItem[] array = [item];
List<TodoItem> list = [item];
```

A bare declaration such as:

```csharp
var items = [item];
```

doesn't provide a target collection type and isn't used by this sample.

Spread elements enumerate their source.

They should not be described as allocation-free without measurement.

## Alias boundary

```csharp
using TodoCounts = (int Total, int Active, int Completed);
```

creates a source-code alias for a tuple type.

It doesn't create a new nominal runtime type.

Use a record or struct when a distinct domain type is required.

## Default-lambda boundary

The formatter declares its lambda with `var`.

The compiler synthesizes a delegate type that preserves the optional parameter.

The sample calls the lambda both with and without the optional prefix.

## Prerequisite

- .NET 10 SDK

## Restore, build, test, and run

```powershell
dotnet restore `
.\CSharp12RefactoringLab.slnx

dotnet build `
.\CSharp12RefactoringLab.slnx `
--configuration Release `
--no-restore

dotnet test `
.\CSharp12RefactoringLab.slnx `
--configuration Release `
--no-build

dotnet run `
--project .\src\CSharp12RefactoringLab\CSharp12RefactoringLab.csproj `
--configuration Release `
--no-build
```

## Expected output

```text
C# 12 Todo Refactoring Lab
Counts: total=4, active=2, completed=2
TODO: Welcome to the C# 12 lab [done]
TODO: Adopt primary constructors [active]
TODO: Use collection expressions [done]
TODO: Try default lambda parameters [active]
TODO: Review explicit properties [done]
```

## Verify the language version

```powershell
dotnet msbuild `
.\src\CSharp12RefactoringLab\CSharp12RefactoringLab.csproj `
-getProperty:LangVersion
```

Expected:

```text
12.0
```

## Project structure

```text
CSharp12RefactoringLab.slnx
README.md
src/
`-- CSharp12RefactoringLab/
|-- CSharp12RefactoringLab.csproj
|-- Program.cs
|-- Formatting/
| `-- TodoFormatter.cs
|-- Models/
| `-- TodoItem.cs
`-- Services/
`-- TodoService.cs
tests/
`-- CSharp12RefactoringLab.Tests/
|-- CSharp12RefactoringLab.Tests.csproj
`-- CSharp12FeatureTests.cs
```

## Deliberately omitted

- inline arrays;
- stack allocation;
- unsafe code;
- benchmarks;
- allocation claims;
- Minimal APIs;
- C# 11 list patterns;
- preview features;
- external services.

Inline arrays are advanced struct-based contiguous storage. They deserve a
separate measured sample rather than a contrived use in this Todo application.

## Verification

- Companion target framework: .NET 10
- Explicit language version: C# 12.0
- Tutorial framework: .NET 8
- Application packages: none
- External services required: none
- Expected tests: 7
- Last reviewed: 2026-08-04

This sample demonstrates language semantics. It doesn't claim universal
performance improvements.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>12.0</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

</Project>
Loading
Loading