SA1201ier 0.1.21
dotnet tool install --global SA1201ier --version 0.1.21
dotnet new tool-manifest
dotnet tool install --local SA1201ier --version 0.1.21
#tool dotnet:?package=SA1201ier&version=0.1.21
nuke :add-package SA1201ier --version 0.1.21
SA1201ier
A command-line tool and MSBuild integration for automatically formatting C# files and now Razor files, with new support for formatting C# code within Razor files, according to StyleCop rule SA1201, which requires elements to be ordered by access level.
Features
- Command-line tool for formatting individual files or entire directories
- Razor file support - formats C# code within
@codeblocks in Razor files - MSBuild integration for automatic formatting during build
- Check mode for CI/CD pipelines to verify formatting without modifying files
- Comprehensive test suite with extensive edge case coverage
- Preserves comments, attributes, and formatting while reordering members
- Respects preprocessor directives and regions - members within
#if/#endifor#region/#endregionblocks are kept together and reordered independently - Flexible hierarchical configuration - use
.sa1201ierrcfiles to configure options per directory - Fully customizable sorting - configure access level order, member type order, and more
- Alphabetical sorting - optionally sort members alphabetically within same group
- Top-level type sorting - optionally sort classes, records, interfaces by access level
- Static/instance control - choose whether static or instance members come first
- Const/non-const control - choose whether const or non-const members come first
What is SA1201?
StyleCop rule SA1201 requires that elements in a C# type be ordered according to their access level and member type. SA1201ier implements this rule with full customization support.
Default Ordering
The default order follows the SA1201 standard:
- Const fields (public → internal → protected internal → protected → private protected → private)
- Static fields (public → internal → protected internal → protected → private protected → private)
- Instance fields (public → internal → protected internal → protected → private protected → private)
- Constructors (public → internal → protected internal → protected → private protected → private)
- Destructors
- Delegates (public → internal → protected internal → protected → private protected → private)
- Events (public → internal → protected internal → protected → private protected → private)
- Enums (public → internal → protected internal → protected → private protected → private)
- Interfaces (public → internal → protected internal → protected → private protected → private)
- Properties (public → internal → protected internal → protected → private protected → private)
- Indexers (public → internal → protected internal → protected → private protected → private)
- Methods (public → internal → protected internal → protected → private protected → private)
- Structs (public → internal → protected internal → protected → private protected → private)
- Classes (public → internal → protected internal → protected → private protected → private)
Customization
All of this is customizable! You can:
- Reverse the access level order (private → public)
- Reorder member types (properties before fields, methods before constructors, etc.)
- Choose whether static or instance members come first
- Choose whether const or non-const members come first
- Sort members alphabetically
- And much more...
See the Configuration section below for details.
Installation
As a .NET Tool (Recommended)
dotnet tool install --global SA1201ier
As an MSBuild Package
Add to your project file:
<ItemGroup>
<PackageReference Include="SA1201ier.MSBuild" Version="1.0.0" PrivateAssets="all" />
</ItemGroup>
Usage
Command Line
Format files
# Format a single file
SA1201ier path/to/YourFile.cs
# Format all C# files in a directory
SA1201ier path/to/your/project
# Check formatting without making changes (useful for CI/CD)
SA1201ier path/to/your/project --check
# Verbose output
SA1201ier path/to/your/project --verbose
# Sort members alphabetically within same access level
SA1201ier path/to/your/project --alphabetical-sort
# Sort top-level types by access level
SA1201ier path/to/your/project --sort-top-level-types
# Create a sample .sa1201ierrc config file
SA1201ier --init-config
Options
<path>- Required. The file or directory path to format or check-c, --check- Check for formatting violations without making changes-v, --verbose- Show detailed output--alphabetical-sort- Sort members alphabetically within same group--sort-top-level-types- Sort top-level types (classes, records, etc.) by access level--init-config- Create a sample .sa1201ierrc configuration file with all available options-h, --help- Show help message
Note: For advanced customization like custom access level order, custom member type order, and static/const preferences, use a .sa1201ierrc configuration file rather than CLI options.
Configuration
SA1201ier supports powerful, flexible configuration through .sa1201ierrc JSON files. You can customize sorting behavior for different parts of your codebase with hierarchical configuration that merges from parent to child directories.
Quick Start
Create a configuration file in your project root:
# Create a sample .sa1201ierrc with all available options
SA1201ier --init-config
This creates a .sa1201ierrc file with comprehensive examples and documentation.
Available Configuration Options
All options are optional and have sensible defaults. You can configure:
Boolean Options
| Option | Default | Description |
|---|---|---|
alphabeticalSort |
false |
Sort members alphabetically within same group |
sortTopLevelTypes |
false |
Sort classes, interfaces, etc. by access level |
staticMembersFirst |
true |
Put static members before instance members |
constMembersFirst |
true |
Put const members before non-const members |
Custom Ordering Arrays
| Option | Description | Valid Values |
|---|---|---|
accessLevelOrder |
Custom order for access modifiers | "Public", "Internal", "ProtectedInternal", "Protected", "PrivateProtected", "Private" |
memberTypeOrder |
Custom order for member types | "Field", "Constructor", "Destructor", "Delegate", "Event", "Enum", "Interface", "Property", "Indexer", "Method", "Struct", "Class" |
topLevelTypeOrder |
Custom order for top-level types | "Enum", "Interface", "Struct", "Class" |
Configuration Examples
Basic Configuration
{
"alphabeticalSort": true,
"sortTopLevelTypes": false
}
Custom Access Level Order (Private-First Style)
Some teams prefer private implementation details at the top:
{
"accessLevelOrder": ["Private", "Protected", "Internal", "Public"],
"staticMembersFirst": false,
"constMembersFirst": false
}
Custom Member Type Order (Properties-First Style)
Useful for DTOs and POCOs:
{
"alphabeticalSort": true,
"memberTypeOrder": ["Property", "Constructor", "Method", "Field"]
}
Complete Custom Configuration
{
"alphabeticalSort": true,
"sortTopLevelTypes": true,
"staticMembersFirst": false,
"constMembersFirst": true,
"accessLevelOrder": ["Private", "Protected", "Internal", "Public"],
"memberTypeOrder": ["Constructor", "Property", "Method", "Field"],
"topLevelTypeOrder": ["Class", "Interface", "Enum", "Struct"]
}
Hierarchical Configuration
SA1201ier searches for .sa1201ierrc files from the file being formatted up to the root directory, merging all configurations it finds. Child directories override parent settings.
Example Directory Structure:
my-project/
├── .sa1201ierrc # Project defaults
├── src/
│ ├── .sa1201ierrc # Override for src/
│ ├── Dtos/
│ │ ├── .sa1201ierrc # Custom order for DTOs
│ │ └── UserDto.cs # Uses Dtos config
│ └── Controllers/
│ ├── .sa1201ierrc # Custom order for Controllers
│ └── UserController.cs # Uses Controllers config
└── tests/
├── .sa1201ierrc # Different rules for tests
└── UserTests.cs
Root .sa1201ierrc:
{
"alphabeticalSort": false,
"sortTopLevelTypes": false
}
src/Dtos/.sa1201ierrc:
{
"alphabeticalSort": true,
"memberTypeOrder": ["Property", "Constructor", "Method"]
}
Effective configuration for UserDto.cs:
alphabeticalSort:true(from Dtos config)sortTopLevelTypes:false(from root config)memberTypeOrder:["Property", "Constructor", "Method"](from Dtos config)
Configuration Priority
Settings are applied in this order (highest priority first):
- Command-line options - Override everything
- Nearest
.sa1201ierrc- In the file's directory - Parent
.sa1201ierrcfiles - Walking up the directory tree - Default values - Built-in defaults
MSBuild Integration
Configure formatting in your .csproj file:
<PropertyGroup>
<SA1201FormatOnBuild>true</SA1201FormatOnBuild>
<SA1201CheckOnBuild>true</SA1201CheckOnBuild>
<SA1201FailOnViolations>true</SA1201FailOnViolations>
</PropertyGroup>
Integration with CSharpier
SA1201ier is designed to work seamlessly with CSharpier. When both packages are installed, SA1201ier automatically runs before CSharpier during the build process. This ensures:
- SA1201ier reorders members according to SA1201 rules
- CSharpier then applies its formatting rules to the reordered code
Recommended setup:
<ItemGroup>
<PackageReference Include="SA1201ier.MSBuild" Version="*" PrivateAssets="all" />
<PackageReference Include="CSharpier.MSBuild" Version="*" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
<SA1201FormatOnBuild>true</SA1201FormatOnBuild>
<CSharpier_Check>false</CSharpier_Check>
</PropertyGroup>
This combination gives you:
- ✅ Consistent member ordering (SA1201ier)
- ✅ Consistent code formatting (CSharpier)
- ✅ Automatic application during build
- ✅ No conflicts between tools
CI/CD Integration
GitHub Actions
name: Check Formatting
on: [push, pull_request]
jobs:
format-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '9.0.x'
- name: Install SA1201ier
run: dotnet tool install --global SA1201ier
- name: Check formatting
run: SA1201ier . --check
Azure Pipelines
- task: DotNetCoreCLI@2
displayName: 'Install SA1201ier'
inputs:
command: 'custom'
custom: 'tool'
arguments: 'install --global SA1201ier'
- task: DotNetCoreCLI@2
displayName: 'Check SA1201 Formatting'
inputs:
command: 'custom'
custom: 'SA1201ier'
arguments: '. --check'
Building from Source
# Clone the repository
git clone https://github.com/ChristopherRWilson/SA1201ier.git
cd SA1201ier
# Build the solution
dotnet build SA1201ier.sln
# Run tests
dotnet test SA1201ier.sln
# Pack for distribution
dotnet pack SA1201ier.sln
Project Structure
- SA1201ier - Command-line interface
- SA1201ier.Core - Core formatting logic using Roslyn
- SA1201ier.MSBuild - MSBuild task integration
- SA1201ier.Tests - Comprehensive test suite
Examples
Before Formatting
public class MyClass
{
private void PrivateMethod() { }
public int PublicProperty { get; set; }
private int PrivateField;
public static int PublicStaticField;
public MyClass() { }
public void PublicMethod() { }
}
After Formatting
public class MyClass
{
public static int PublicStaticField;
private int PrivateField;
public MyClass() { }
public int PublicProperty { get; set; }
public void PublicMethod() { }
private void PrivateMethod() { }
}
Preprocessor Directives and Regions
SA1201ier respects preprocessor directives and region boundaries. Members within these blocks are treated as separate groups and reordered independently:
// Before
public class MyClass
{
#region Private Fields
private int field2;
private int field1;
#endregion
#region Public Fields
public int publicField2;
public int publicField1;
#endregion
#if DEBUG
private void DebugMethod() { }
#endif
private void PrivateMethod() { }
public void PublicMethod() { }
}
// After
public class MyClass
{
#region Private Fields
private int field2;
private int field1;
#endregion
#region Public Fields
public int publicField2;
public int publicField1;
#endregion
#if DEBUG
private void DebugMethod() { }
#endif
public void PublicMethod() { }
private void PrivateMethod() { }
}
Note: Members within regions and preprocessor blocks maintain their relative order, while members outside these blocks are reordered according to SA1201 rules.
Razor File Support
SA1201ier automatically detects and formats C# code within @code blocks in Razor files (.razor). The formatter:
- Extracts all
@codeblocks from the Razor file - Formats the C# code according to SA1201 rules
- Preserves all Razor markup and directives
- Reinserts the formatted code back into the Razor file
Example:
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
// Before formatting
private void IncrementCount()
{
currentCount++;
}
public int currentCount = 0;
private void Reset()
{
currentCount = 0;
}
}
After formatting:
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
// After formatting - fields before methods, public before private
public int currentCount = 0;
public void IncrementCount()
{
currentCount++;
}
private void Reset()
{
currentCount = 0;
}
}
Usage:
# Format a single Razor file
SA1201ier MyComponent.razor
# Format all C# and Razor files in a directory
SA1201ier ./Pages
# Check Razor files without formatting
SA1201ier ./Pages --check
Note: SA1201ier processes only the C# code within @code blocks. All other Razor syntax (HTML, directives, inline C# expressions) is preserved unchanged.
Alphabetical Sorting
Enable alphabetical sorting within the same group of members:
Configuration:
{
"alphabeticalSort": true
}
Or use CLI:
SA1201ier path/to/your/project --alphabetical-sort
Example:
// Before
public class MyClass
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
// After (with alphabetical sorting)
public class MyClass
{
public int Age { get; set; }
public string Email { get; set; }
public string Name { get; set; }
}
Use Cases:
- DTOs with many properties become easier to navigate
- Reduces merge conflicts when multiple developers add properties
- Makes finding specific members easier in large classes
Top-Level Type Sorting
Sort classes, interfaces, enums, and structs by access level:
Configuration:
{
"sortTopLevelTypes": true
}
Or use CLI:
SA1201ier path/to/your/project --sort-top-level-types
Example:
// Before
namespace MyApp;
internal class InternalHelper { }
public class PublicApi { }
public record PublicRecord { }
internal record InternalRecord { }
// After (with top-level type sorting)
namespace MyApp;
public class PublicApi { }
public record PublicRecord { }
internal record InternalRecord { }
internal class InternalHelper { }
Use Cases:
- Files with multiple type definitions
- Ensures public API appears first
- Makes file structure more predictable
Static vs Instance Members
Control whether static members appear before or after instance members:
Configuration:
{
"staticMembersFirst": false
}
Example:
// With staticMembersFirst: true (default)
public class MyClass
{
public static int StaticField;
public int InstanceField;
}
// With staticMembersFirst: false
public class MyClass
{
public int InstanceField;
public static int StaticField;
}
Const vs Non-Const Members
Control whether const members appear before or after non-const members:
Configuration:
{
"constMembersFirst": false
}
Example:
// With constMembersFirst: true (default)
public class MyClass
{
public const int MaxValue = 100;
public int Value;
}
// With constMembersFirst: false
public class MyClass
{
public int Value;
public const int MaxValue = 100;
}
Custom Access Level Order
Define your own access level ordering. The default follows SA1201 (public → private), but you can reverse it or use any custom order:
Configuration:
{
"accessLevelOrder": ["Private", "Protected", "Internal", "Public"]
}
Example:
// With custom order above
public class MyClass
{
private int _privateField;
protected int ProtectedField;
internal int InternalField;
public int PublicField;
}
Use Cases:
- Private-first development style
- Implementation-before-interface coding
- Custom team conventions
Custom Member Type Order
Define your own member type ordering. The default follows SA1201 (fields → constructors → properties → methods), but you can customize it:
Configuration:
{
"memberTypeOrder": ["Constructor", "Property", "Method", "Field"]
}
Example:
// With custom order above
public class MyClass
{
// Constructors first
public MyClass() { }
// Then properties
public int Id { get; set; }
public string Name { get; set; }
// Then methods
public void DoSomething() { }
// Then fields
private int _field;
}
Use Cases:
- DTOs where properties should be prominent
- Test classes where setup methods should be first
- Custom team conventions
Custom Top-Level Type Order
Define your own top-level type ordering:
Configuration:
{
"sortTopLevelTypes": true,
"topLevelTypeOrder": ["Class", "Interface", "Enum", "Struct"]
}
Example:
// With custom order above
namespace MyApp;
// Classes first
public class MyClass { }
// Then interfaces
public interface IMyInterface { }
// Then enums
public enum MyEnum { }
// Then structs
public struct MyStruct { }
Real-World Configuration Examples
Example 1: DTO Configuration
For Data Transfer Objects, prioritize properties and enable alphabetical sorting:
{
"alphabeticalSort": true,
"memberTypeOrder": ["Property", "Constructor", "Method", "Field"],
"staticMembersFirst": false
}
Example 2: Private-First Development Style
Some teams prefer seeing private implementation first:
{
"accessLevelOrder": [
"Private",
"PrivateProtected",
"Protected",
"ProtectedInternal",
"Internal",
"Public"
],
"constMembersFirst": false,
"staticMembersFirst": false
}
Example 3: Test Class Configuration
Test classes often benefit from different organization:
{
"alphabeticalSort": true,
"memberTypeOrder": ["Field", "Constructor", "Method", "Property"]
}
Example 4: Controller Configuration
For MVC/API controllers:
{
"alphabeticalSort": false,
"memberTypeOrder": ["Constructor", "Method", "Field", "Property"],
"accessLevelOrder": ["Public", "Private", "Protected", "Internal"]
}
Using Configuration in Your Workflow
For New Projects
Start with a project-wide configuration:
cd my-project
SA1201ier --init-config
# Edit .sa1201ierrc to your preferences
git add .sa1201ierrc
git commit -m "Add SA1201ier configuration"
For Existing Projects
Use hierarchical configuration for gradual adoption:
my-project/
├── .sa1201ierrc # Minimal changes by default
└── src/
└── NewFeature/
└── .sa1201ierrc # Strict formatting for new code
For Mixed Codebases
Different rules for different areas:
my-project/
├── .sa1201ierrc # Project defaults
├── src/
│ ├── Dtos/
│ │ └── .sa1201ierrc # Alphabetical, properties-first
│ ├── Controllers/
│ │ └── .sa1201ierrc # Public methods first
│ └── Models/
│ └── .sa1201ierrc # Standard SA1201
└── tests/
└── .sa1201ierrc # Alphabetical for tests
Command-Line Overrides
Command-line options override all configuration files:
# Override config files with CLI options
SA1201ier ./src --alphabetical-sort
# Test configuration before committing
SA1201ier ./src/Models --alphabetical-sort --check
# Apply multiple options
SA1201ier ./src --alphabetical-sort --sort-top-level-types
Best Practices
- Start Simple - Begin with default settings and add customization as needed
- Document Your Choices - Use JSON comments to explain why you chose specific settings
- Use Hierarchical Config - Define project defaults at root, override for specific areas
- Test Before Committing - Use
--checkto preview changes before applying - Version Control - Always commit
.sa1201ierrcfiles to ensure team consistency
Troubleshooting
Configuration Not Working
- Ensure the file is named exactly
.sa1201ierrc(with leading dot) - Verify JSON syntax is valid (use a JSON validator or check for common errors)
- Use
--verboseto see which config files are being loaded - Remember: CLI options override all config files
Finding the Right Configuration
If you're unsure what configuration to use:
- Create a sample config:
SA1201ier --init-config - Test different options:
SA1201ier ./src --alphabetical-sort --check - Apply what works: Copy successful CLI options to your
.sa1201ierrc
Unexpected Ordering
- Check for multiple
.sa1201ierrcfiles in parent directories - Verify custom order arrays have correct values (case-sensitive)
- Use
--verboseto see which configuration is being applied
Future Enhancements
- Integration with popular formatters like CSharpier
- Support for additional StyleCop ordering rules
- Visual Studio extension
preserveRegionOrderoption to prevent reordering within regionsignorePatternsfor excluding files/directories
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the GPL3 License - see the LICENSE file for details.
Acknowledgments
- Based on StyleCop Analyzers SA1201 rule
- Inspired by CSharpier
Related Projects
- StyleCop Analyzers - Comprehensive StyleCop rule analyzers
- CSharpier - Opinionated C# code formatter
- dotnet-format - Official .NET code formatter
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.21 | 210 | 10/11/2025 |
| 0.1.20 | 145 | 10/10/2025 |
| 0.1.19 | 139 | 10/10/2025 |
| 0.1.18 | 145 | 10/10/2025 |
| 0.1.17 | 155 | 10/10/2025 |
| 0.1.16 | 166 | 10/10/2025 |
| 0.1.15 | 145 | 10/10/2025 |
| 0.1.14 | 211 | 10/8/2025 |
| 0.1.13 | 209 | 10/8/2025 |
| 0.1.12 | 228 | 10/8/2025 |
| 0.1.11 | 206 | 10/8/2025 |
| 0.1.10 | 212 | 10/8/2025 |
| 0.1.9 | 214 | 10/8/2025 |
| 0.1.8 | 223 | 10/8/2025 |
| 0.1.7 | 214 | 10/8/2025 |
| 0.1.6 | 213 | 10/8/2025 |
| 0.1.5 | 224 | 10/8/2025 |
| 0.1.4 | 215 | 10/8/2025 |
| 0.1.3 | 210 | 10/8/2025 |
| 0.1.2 | 224 | 10/8/2025 |