Converting all classes to filescoped namepspaces
- Understanding the Main Section of .editorconfig
- Enforcing Code Style with .editorconfig and Static Analysis in .NET
- Customize AI Git Commit Messages with GitHub Copilot in Visual Studio
- Organizing Usings with .editorconfig and Static Analysis in .NET
- Code-Block Preferences with .editorconfig and Static Analysis in .NET
- Converting all classes to filescoped namepspaces (this post)
Converting C# Namespaces to File-Scoped Using PowerShell
The Problem
When trying to enforce the IDE0161 rule (Use file-scoped namespace declarations) across a large codebase, Visual Studio’s built-in code cleanup feature proved insufficient.
With over 6000 classes to convert, manual modification wasn’t practical. After attempting to use Visual Studio’s built-in tools and finding them inadequate for this scale, I decided to create a PowerShell script that could handle this conversion automatically.
Important Notes
- Excludes .NET Standard 2.0 projects (which don’t support file-scoped namespaces)
- Preserves documentation comments and formatting
- Provides detailed logging of the conversion process
The Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Write-Host "Starting namespace conversion script..." -ForegroundColor Cyan
# Find directories containing .NET Standard 2.0 projects
$projectFiles = Get-ChildItem -Path . -Filter *.csproj -Recurse
$excludeDirs = @()
foreach ($proj in $projectFiles) {
$content = Get-Content $proj.FullName -Raw
if ($content -match '<TargetFramework>netstandard2.0</TargetFramework>') {
Write-Host "Excluding .NET Standard 2.0 project: $($proj.FullName)" -ForegroundColor Yellow
$excludeDirs += $proj.Directory.FullName
}
}
# Get all .cs files excluding .NET Standard 2.0 project directories
$files = Get-ChildItem -Path . -Filter *.cs -Recurse |
Where-Object {
$file = $_
-not ($excludeDirs | Where-Object { $file.FullName.StartsWith($_) })
}
Write-Host "Found $($files.Count) .cs files to process (excluding .NET Standard 2.0 projects)" -ForegroundColor Green
$converted = 0
$skipped = 0
foreach ($file in $files) {
Write-Host "`nProcessing: $($file.FullName)" -ForegroundColor Yellow
$content = Get-Content $file.FullName -Raw
if ($content -match "namespace .+;") {
Write-Host " Skipped: Already using file-scoped namespace" -ForegroundColor Gray
$skipped++
continue
}
$newContent = $content -replace '(namespace [^\s{]+)\s*\{(\r?\n[\s\S]+?)\}[\s]*$', '$1;$2'
$newContent = $newContent -replace '(\r?\n\s*){3,}', "`n`n"
if ($newContent -ne $content) {
$newContent | Set-Content $file.FullName -NoNewline
Write-Host " Success: Converted to file-scoped namespace" -ForegroundColor Green
$converted++
} else {
Write-Host " Skipped: No namespace found or no changes needed" -ForegroundColor Gray
$skipped++
}
}
Write-Host "`nConversion Complete!" -ForegroundColor Cyan
Write-Host "Files converted: $converted" -ForegroundColor Green
Write-Host "Files skipped: $skipped" -ForegroundColor Yellow
The Script Explained
Initial Setup and Project Filtering
1 2 3 4
Write-Host "Starting namespace conversion script..." -ForegroundColor Cyan # Find and exclude .NET Standard 2.0 projects $projectFiles = Get-ChildItem -Path . -Filter *.csproj -Recurse $excludeDirs = @()
This section initializes the script and creates an array to store directories to exclude.
Finding .NET Standard Projects
1 2 3 4 5 6 7
foreach ($proj in $projectFiles) { $content = Get-Content $proj.FullName -Raw if ($content -match '<TargetFramework>netstandard2.0</TargetFramework>') { Write-Host "Excluding .NET Standard 2.0 project: $($proj.FullName)" -ForegroundColor Yellow $excludeDirs += $proj.Directory.FullName } }
This loop identifies all .NET Standard 2.0 projects by checking their project files and adds their directories to the exclusion list.
File Collection
1 2 3 4 5
$files = Get-ChildItem -Path . -Filter *.cs -Recurse | Where-Object { $file = $_ -not ($excludeDirs | Where-Object { $file.FullName.StartsWith($_) }) }
Gets all .cs files except those in excluded directories.
- Processing Loop
1 2 3 4 5 6 7 8 9
foreach ($file in $files) { # Read file content $content = Get-Content $file.FullName -Raw # Skip if already using file-scoped namespace if ($content -match "namespace .+;") { # ...logging code... continue }
Reads each file and checks if it already uses file-scoped namespaces.
- Conversion Logic
1 2 3 4
# Convert namespace syntax $newContent = $content -replace '(namespace [^\s{]+)\s*\{(\r?\n[\s\S]+?)\}[\s]*$', '$1;$2' # Clean up extra blank lines $newContent = $newContent -replace '(\r?\n\s*){3,}', "`n`n"
The core conversion using regex patterns:
- First pattern converts traditional namespace to file-scoped
(namespace [^\s{]+)\s*\{(\r?\n[\s\S]+?)\}[\s]*$
: This pattern matches the traditional namespace declaration and captures the namespace name and its contents. Thenamespace [^\s{]+
part matches the namespace keyword followed by the namespace name. The\s*\{
part matches any whitespace followed by an opening brace. The(\r?\n[\s\S]+?)\}
part captures the contents of the namespace, including newlines, until the closing brace. The[\s]*$
part matches any trailing whitespace at the end of the file.$1;$2
: This replacement inserts a semicolon after the namespace name and retains the captured contents.
- Second pattern cleans up excessive blank lines
(\r?\n\s*){3,}
: This pattern matches three or more consecutive blank lines.- “
n
n”: This replacement reduces the matched blank lines to two newlines.
- First pattern converts traditional namespace to file-scoped
- File Update
1 2 3 4
if ($newContent -ne $content) { $newContent | Set-Content $file.FullName -NoNewline # ...logging code... }
Saves the changes only if the content was actually modified.
- Summary Output
1 2 3
Write-Host "`nConversion Complete!" -ForegroundColor Cyan Write-Host "Files converted: $converted" -ForegroundColor Green Write-Host "Files skipped: $skipped" -ForegroundColor Yellow
Provides a summary of the conversion process.
Example Transformation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Before
namespace MyApp.Features
{
public class MyClass
{
}
}
// After
namespace MyApp.Features;
public class MyClass
{
}
Success Story
The script proved to be a highly effective solution. In less than 2 minutes, it successfully processed a large enterprise codebase containing over 6000 files across multiple projects. The conversion was clean, maintained all code formatting, and correctly excluded incompatible .NET Standard projects. This automated approach not only saved days of manual work but also eliminated the risk of human error during the conversion process.
What initially seemed like a daunting task was solved with a simple yet powerful PowerShell script. The logging feature provided clear visibility into the conversion process, making it easy to verify that all files were processed correctly.
This script successfully automated the conversion of thousands of files while maintaining code integrity and providing clear feedback throughout the process.