diff --git a/.github/workflows/build-samples.yml b/.github/workflows/build-samples.yml
index 992d132..cca3564 100644
--- a/.github/workflows/build-samples.yml
+++ b/.github/workflows/build-samples.yml
@@ -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:
@@ -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:
@@ -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"
diff --git a/README.md b/README.md
index ba3cf29..5feb6c9 100644
--- a/README.md
+++ b/README.md
@@ -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/)
@@ -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
diff --git a/csharp-language/csharp-12-features/CSharp12RefactoringLab.slnx b/csharp-language/csharp-12-features/CSharp12RefactoringLab.slnx
new file mode 100644
index 0000000..2a77b17
--- /dev/null
+++ b/csharp-language/csharp-12-features/CSharp12RefactoringLab.slnx
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/csharp-language/csharp-12-features/README.md b/csharp-language/csharp-12-features/README.md
new file mode 100644
index 0000000..8807088
--- /dev/null
+++ b/csharp-language/csharp-12-features/README.md
@@ -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
+12.0
+```
+
+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` 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 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.
\ No newline at end of file
diff --git a/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/CSharp12RefactoringLab.csproj b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/CSharp12RefactoringLab.csproj
new file mode 100644
index 0000000..27eb670
--- /dev/null
+++ b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/CSharp12RefactoringLab.csproj
@@ -0,0 +1,12 @@
+
+
+
+ Exe
+ net10.0
+ 12.0
+ enable
+ enable
+ true
+
+
+
\ No newline at end of file
diff --git a/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Formatting/TodoFormatter.cs b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Formatting/TodoFormatter.cs
new file mode 100644
index 0000000..5921045
--- /dev/null
+++ b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Formatting/TodoFormatter.cs
@@ -0,0 +1,41 @@
+using CSharp12RefactoringLab.Models;
+
+namespace CSharp12RefactoringLab.Formatting;
+
+public static class TodoFormatter
+{
+ public static string[] BuildLabels(
+ IEnumerable items,
+ string? explicitPrefix = null)
+ {
+ var format =
+ (
+ TodoItem item,
+ string prefix = "TODO"
+ ) =>
+ $"{prefix}: {item.Title} [{GetState(item)}]";
+
+ return explicitPrefix is null
+ ?
+ [
+ .. items.Select(
+ item =>
+ format(
+ item))
+ ]
+ :
+ [
+ .. items.Select(
+ item =>
+ format(
+ item,
+ explicitPrefix))
+ ];
+ }
+
+ private static string GetState(
+ TodoItem item) =>
+ item.IsComplete
+ ? "done"
+ : "active";
+}
\ No newline at end of file
diff --git a/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Models/TodoItem.cs b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Models/TodoItem.cs
new file mode 100644
index 0000000..ee07e00
--- /dev/null
+++ b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Models/TodoItem.cs
@@ -0,0 +1,31 @@
+namespace CSharp12RefactoringLab.Models;
+
+public sealed class TodoItem(
+ string title,
+ bool isComplete = false)
+{
+ public string Title { get; } =
+ NormalizeTitle(
+ title);
+
+ public bool IsComplete { get; } =
+ isComplete;
+
+ public TodoItem Complete() =>
+ IsComplete
+ ? this
+ : new TodoItem(
+ Title,
+ isComplete:
+ true);
+
+ private static string NormalizeTitle(
+ string value)
+ {
+ ArgumentException
+ .ThrowIfNullOrWhiteSpace(
+ value);
+
+ return value.Trim();
+ }
+}
\ No newline at end of file
diff --git a/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Program.cs b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Program.cs
new file mode 100644
index 0000000..4c51851
--- /dev/null
+++ b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Program.cs
@@ -0,0 +1,45 @@
+using CSharp12RefactoringLab.Formatting;
+using CSharp12RefactoringLab.Models;
+using CSharp12RefactoringLab.Services;
+
+TodoItem[] seedItems =
+[
+ new TodoItem(
+ "Adopt primary constructors"),
+
+ new TodoItem(
+ "Use collection expressions",
+ isComplete:
+ true)
+];
+
+var service =
+ new TodoService(
+ seedItems);
+
+service.Add(
+ new TodoItem(
+ "Try default lambda parameters"));
+
+service.Add(
+ new TodoItem(
+ "Review explicit properties",
+ isComplete:
+ true));
+
+var counts =
+ service.GetCounts();
+
+Console.WriteLine(
+ "C# 12 Todo Refactoring Lab");
+
+Console.WriteLine(
+ $"Counts: total={counts.Total}, active={counts.Active}, completed={counts.Completed}");
+
+foreach (string label in
+ TodoFormatter.BuildLabels(
+ service.GetAllWithWelcome()))
+{
+ Console.WriteLine(
+ label);
+}
\ No newline at end of file
diff --git a/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Services/TodoService.cs b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Services/TodoService.cs
new file mode 100644
index 0000000..8fcac1d
--- /dev/null
+++ b/csharp-language/csharp-12-features/src/CSharp12RefactoringLab/Services/TodoService.cs
@@ -0,0 +1,88 @@
+using CSharp12RefactoringLab.Models;
+
+using TodoCounts =
+ (
+ int Total,
+ int Active,
+ int Completed
+ );
+
+namespace CSharp12RefactoringLab.Services;
+
+public sealed class TodoService(
+ IEnumerable seedItems)
+{
+ private readonly List
+ _items =
+ [
+ .. seedItems
+ ];
+
+ public void Add(
+ TodoItem item)
+ {
+ ArgumentNullException
+ .ThrowIfNull(
+ item);
+
+ _items.Add(
+ item);
+ }
+
+ public TodoItem[] Snapshot() =>
+ [
+ .. _items
+ ];
+
+ public TodoItem[] GetActive(
+ int limit = 10)
+ {
+ ArgumentOutOfRangeException
+ .ThrowIfNegativeOrZero(
+ limit);
+
+ return
+ [
+ .. _items
+ .Where(
+ item =>
+ !item.IsComplete)
+ .Take(
+ limit)
+ ];
+ }
+
+ public TodoItem[] GetAllWithWelcome()
+ {
+ TodoItem welcome =
+ new(
+ "Welcome to the C# 12 lab",
+ isComplete:
+ true);
+
+ return
+ [
+ welcome,
+ .. _items
+ ];
+ }
+
+ public TodoCounts GetCounts()
+ {
+ int completed =
+ _items.Count(
+ item =>
+ item.IsComplete);
+
+ return (
+ Total:
+ _items.Count,
+
+ Active:
+ _items.Count
+ - completed,
+
+ Completed:
+ completed);
+ }
+}
\ No newline at end of file
diff --git a/csharp-language/csharp-12-features/tests/CSharp12RefactoringLab.Tests/CSharp12FeatureTests.cs b/csharp-language/csharp-12-features/tests/CSharp12RefactoringLab.Tests/CSharp12FeatureTests.cs
new file mode 100644
index 0000000..fc6cbfa
--- /dev/null
+++ b/csharp-language/csharp-12-features/tests/CSharp12RefactoringLab.Tests/CSharp12FeatureTests.cs
@@ -0,0 +1,249 @@
+using CSharp12RefactoringLab.Formatting;
+using CSharp12RefactoringLab.Models;
+using CSharp12RefactoringLab.Services;
+
+namespace CSharp12RefactoringLab.Tests;
+
+public sealed class CSharp12FeatureTests
+{
+ [Fact]
+ public void
+ Primary_constructor_initializes_explicit_properties()
+ {
+ var item =
+ new TodoItem(
+ " Learn C# 12 ",
+ isComplete:
+ true);
+
+ Assert.Equal(
+ "Learn C# 12",
+ item.Title);
+
+ Assert.True(
+ item.IsComplete);
+
+ Assert.Throws<
+ ArgumentException>(
+ () =>
+ new TodoItem(
+ " "));
+ }
+
+ [Fact]
+ public void
+ Primary_constructor_parameters_are_not_public_properties()
+ {
+ Type itemType =
+ typeof(
+ TodoItem);
+
+ Assert.Null(
+ itemType.GetProperty(
+ "title"));
+
+ Assert.Null(
+ itemType.GetProperty(
+ "isComplete"));
+
+ Assert.NotNull(
+ itemType.GetProperty(
+ nameof(
+ TodoItem.Title)));
+
+ Assert.NotNull(
+ itemType.GetProperty(
+ nameof(
+ TodoItem.IsComplete)));
+ }
+
+ [Fact]
+ public void
+ Collection_expressions_copy_seed_and_snapshot_data()
+ {
+ TodoItem[] seed =
+ [
+ new TodoItem(
+ "First")
+ ];
+
+ var service =
+ new TodoService(
+ seed);
+
+ seed[0] =
+ new TodoItem(
+ "Changed outside");
+
+ TodoItem[] firstSnapshot =
+ service.Snapshot();
+
+ firstSnapshot[0] =
+ new TodoItem(
+ "Changed snapshot");
+
+ TodoItem[] secondSnapshot =
+ service.Snapshot();
+
+ Assert.Equal(
+ "First",
+ secondSnapshot[0]
+ .Title);
+ }
+
+ [Fact]
+ public void
+ Spread_expression_prepends_welcome_without_mutating_service()
+ {
+ var service =
+ new TodoService(
+ [
+ new TodoItem(
+ "Stored item")
+ ]);
+
+ TodoItem[] composed =
+ service.GetAllWithWelcome();
+
+ TodoItem[] stored =
+ service.Snapshot();
+
+ Assert.Equal(
+ 2,
+ composed.Length);
+
+ Assert.Equal(
+ "Welcome to the C# 12 lab",
+ composed[0].Title);
+
+ Assert.Single(
+ stored);
+
+ Assert.Equal(
+ "Stored item",
+ stored[0].Title);
+ }
+
+ [Fact]
+ public void
+ Tuple_alias_returns_named_counts()
+ {
+ var service =
+ new TodoService(
+ [
+ new TodoItem(
+ "Active"),
+
+ new TodoItem(
+ "Completed",
+ isComplete:
+ true),
+
+ new TodoItem(
+ "Also active")
+ ]);
+
+ var counts =
+ service.GetCounts();
+
+ Assert.Equal(
+ 3,
+ counts.Total);
+
+ Assert.Equal(
+ 2,
+ counts.Active);
+
+ Assert.Equal(
+ 1,
+ counts.Completed);
+ }
+
+ [Fact]
+ public void
+ Default_lambda_parameter_and_explicit_override_format_labels()
+ {
+ TodoItem[] items =
+ [
+ new TodoItem(
+ "Write tests"),
+
+ new TodoItem(
+ "Ship sample",
+ isComplete:
+ true)
+ ];
+
+ string[] defaults =
+ TodoFormatter.BuildLabels(
+ items);
+
+ string[] custom =
+ TodoFormatter.BuildLabels(
+ items,
+ explicitPrefix:
+ "TASK");
+
+ Assert.Equal(
+ "TODO: Write tests [active]",
+ defaults[0]);
+
+ Assert.Equal(
+ "TODO: Ship sample [done]",
+ defaults[1]);
+
+ Assert.Equal(
+ "TASK: Write tests [active]",
+ custom[0]);
+
+ Assert.Equal(
+ "TASK: Ship sample [done]",
+ custom[1]);
+ }
+
+ [Fact]
+ public void
+ Active_filter_applies_limit_and_preserves_order()
+ {
+ var service =
+ new TodoService(
+ [
+ new TodoItem(
+ "First active"),
+
+ new TodoItem(
+ "Completed",
+ isComplete:
+ true),
+
+ new TodoItem(
+ "Second active"),
+
+ new TodoItem(
+ "Third active")
+ ]);
+
+ TodoItem[] active =
+ service.GetActive(
+ limit:
+ 2);
+
+ Assert.Collection(
+ active,
+ first =>
+ Assert.Equal(
+ "First active",
+ first.Title),
+ second =>
+ Assert.Equal(
+ "Second active",
+ second.Title));
+
+ Assert.Throws<
+ ArgumentOutOfRangeException>(
+ () =>
+ service.GetActive(
+ limit:
+ 0));
+ }
+}
\ No newline at end of file
diff --git a/csharp-language/csharp-12-features/tests/CSharp12RefactoringLab.Tests/CSharp12RefactoringLab.Tests.csproj b/csharp-language/csharp-12-features/tests/CSharp12RefactoringLab.Tests/CSharp12RefactoringLab.Tests.csproj
new file mode 100644
index 0000000..225270f
--- /dev/null
+++ b/csharp-language/csharp-12-features/tests/CSharp12RefactoringLab.Tests/CSharp12RefactoringLab.Tests.csproj
@@ -0,0 +1,47 @@
+
+
+
+ net10.0
+ 12.0
+ enable
+ enable
+ true
+ false
+ true
+ Exe
+
+
+
+
+
+
+
+
+ all
+
+ runtime;
+ build;
+ native;
+ contentfiles;
+ analyzers;
+ buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file