Skip to main content

GST C# Naming Convention Standards

This document defines the C# naming conventions for the GatherSystemTech team, based on Microsoft .NET naming guidelines with team-specific conventions.


1. Project Naming (.csproj)

1.1 Base Framework Projects

Format: GST.{Layer}.{Module}

LayerFormatExample
Core AbstractionsGST.Core.{Domain}GST.Core.Abstractions, GST.Core.Common
Feature PluginsGST.Plugin.{FeatureName}GST.Plugin.UserManagement, GST.Plugin.AuditTrail
Protocol PluginsGST.Plugin.{Protocol}GST.Plugin.Modbus, GST.Plugin.S7
UI AbstractionsGST.UI.{Framework}GST.UI.Wpf, GST.UI.WinForms
Service InfrastructureGST.ServiceInfrastructure.{Component}GST.ServiceInfrastructure.Host
ToolsGST.Tools.{ToolName}GST.Tools.LocalizationGenerator

1.2 Application Layer Projects

General application format: GST.App.{ProjectName}

GST.App.ProcessVision           # UI layer
GST.App.ProcessVision.Core # Business logic layer

Company-specific application format: {Company}.{ProjectName}

When an application needs to be published under a client/partner company brand, a company prefix is allowed:

Jope.SMB.Core                   # Jope SMB business logic
Jope.SMB.WPF # Jope SMB UI layer
Leyu.{ProjectName} # Leyu projects

Note: This rule applies to subcontracted projects where the software must display the client company name externally.

1.3 Test Projects

Format: {OriginalProject}.Tests

GST.Core.Common.Tests
GST.Plugin.UserManagement.Tests
Jope.SMB.Core.Tests

2. Namespaces

2.1 Format

Standard format: {ProjectName}.{Subdomain}

// Base framework
namespace GST.Core.Abstractions.Audit;
namespace GST.Plugin.UserManagement.Services;
namespace GST.Plugin.Modbus.Rtu;

// Application layer
namespace GST.App.ProcessVision.ViewModels;
namespace Jope.SMB.Core.Devices;
namespace Jope.SMB.WPF.Services;

2.2 Rules

  • Maximum 4 levels deep
  • Must correspond to folder structure
  • Use file-scoped namespaces (C# 10+)
// ✅ Correct - File-scoped namespace
namespace GST.Core.Abstractions.Audit;

public interface IAuditService { }

// ❌ Avoid - Block-scoped namespace
namespace GST.Core.Abstractions.Audit
{
public interface IAuditService { }
}

3. File Naming

All files use PascalCase, named after the primary class/interface.

TypeFormatExample
InterfaceI{ConceptName}.csIUserService.cs, ICacheService.cs
Service{Concept}Service.csUserService.cs, MemoryCacheService.cs
Repository{Entity}Repository.csUserRepository.cs
Entity{Concept}Entity.csUserEntity.cs, RoleEntity.cs
Request DTO{Action}{Concept}Request.csCreateUserRequest.cs
Response DTO{Concept}Response.csLoginResponse.cs
Exception{Concept}Exception.csModbusException.cs
Extension Methods{TargetType}Extensions.csStringExtensions.cs
Helper Utilities{Concept}Helper.csRetryHelper.cs
Options/Configuration{Concept}Options.csAuthenticationOptions.cs
Factory{Concept}Factory.csControllerFactory.cs
Enum{Concept}.cs (singular)AlarmSeverity.cs
ViewModel{Name}ViewModel.csMainViewModel.cs
Converter{Name}Converter.csBoolToVisibilityConverter.cs

3.1 Helper vs Extensions Distinction

TypePurposeExample
ExtensionsExtension methods (with this parameter)StringExtensions.cs
HelperStatic utility methods (without this parameter)RetryHelper.cs
// Extensions - Extension methods
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string value) => string.IsNullOrEmpty(value);
}

// Helper - Static utility methods
public static class RetryHelper
{
public static async Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> action, int maxRetries) { }
}

4. Class/Interface Naming

4.1 Naming Patterns

TypeFormatExample
InterfaceI{ConceptName}IUserService, ICacheService
Abstract Class{Concept}BaseViewModelBase, PresenterBase
Service{Concept}ServiceUserService, RoleService
Repository{Entity}RepositoryUserRepository
Factory{Concept}FactoryControllerFactory
Controller (Device){DeviceName}ControllerTaieFcController
Manager{Concept}ManagerAlarmManager
Builder{Concept}BuilderCommandBuilder
Handler{Concept}HandlerMessageHandler

4.2 Service vs Manager vs Handler Distinction

TypeResponsibilityExample
ServiceBusiness logic (CRUD, validation, computation)UserService, AuthenticationService
ManagerResource management (lifecycle, state, pooling)ConnectionManager, AlarmManager
HandlerEvent/message handling (responds to specific events)MessageHandler, ErrorHandler

4.3 Access Modifiers

ModifierPurpose
public interfaceExternal contract
public abstract classInheritable base class
public sealed classNon-inheritable public class
internal classInternal implementation (most common)
internal sealed classInternal non-inheritable

5. Method Naming

5.1 Naming Patterns

OperationFormatExample
Get singleGet{Entity}[By{Key}]AsyncGetUserByIdAsync
Get multipleGet{Entities}AsyncGetUsersAsync, GetAllRolesAsync
CreateCreate{Entity}AsyncCreateUserAsync
UpdateUpdate{Entity}AsyncUpdateUserAsync
DeleteDelete{Entity}AsyncDeleteUserAsync
ValidateValidate{Concept}AsyncValidatePasswordAsync
VerifyVerify{Concept}AsyncVerifyChainAsync
Check existence{Entity}ExistsAsyncUserExistsAsync
Try operationTry{Action}TryParse, TryGetValue
Event handlerOn{EventName}OnConnected, OnError

5.2 Async Method Rules

  • All async methods must end with Async
  • Must include a CancellationToken parameter
// ✅ Correct
public async Task<User> GetUserByIdAsync(
Guid id,
CancellationToken cancellationToken = default);

// ❌ Wrong - Missing Async suffix and CancellationToken
public async Task<User> GetUserById(Guid id);

6. Variable Naming

6.1 Naming Rules

TypeFormatExample
Private field_camelCase_userRepository, _logger
Public propertyPascalCaseUsername, IsActive
ParametercamelCaseuserId, cancellationToken
Local variablecamelCaseuser, result
ConstantPascalCaseMaxRetryCount, DefaultTimeout
Static readonlyPascalCaseEmpty, Default

6.2 Boolean Naming

Use Is/Has/Can/Should prefixes:

// ✅ Correct
public bool IsActive { get; set; }
public bool HasPermission { get; set; }
public bool CanExecute { get; set; }
public bool ShouldRetry { get; set; }

// ❌ Avoid
public bool Active { get; set; } // Unclear if verb or adjective
public bool Permission { get; set; } // Unclear what type it represents

7. Enum Naming

7.1 Regular Enums

Use singular names with PascalCase values:

public enum AlarmSeverity
{
Critical = 0,
High = 1,
Medium = 2,
Low = 3
}

public enum ChannelState
{
Disconnected = 0,
Connecting = 1,
Connected = 2,
Error = 3
}

7.2 Flags Enums

Use plural names:

[Flags]
public enum FilePermissions
{
None = 0,
Read = 1,
Write = 2,
Execute = 4,
All = Read | Write | Execute
}

8. Folder Structure

8.1 Base Project Structure

GST.Plugin.{FeatureName}/
├── Abstractions/ # Interface definitions
├── Models/ # Data models (Entity, DTO)
├── Services/ # Service implementations
├── Repositories/ # Data access
├── Extensions/ # Extension methods
├── Helpers/ # Utility helpers
├── Exceptions/ # Custom exceptions
└── Configuration/ # Configuration related

8.2 Application Layer Project Structure

{App}.WPF/                      # UI layer
├── Controls/ # Custom controls
├── Converters/ # Value converters
├── Resources/ # Resource files
│ ├── Icons/
│ └── Localization/
├── Services/ # UI services
├── ViewModels/ # ViewModels
├── Views/ # Views (XAML)
└── Themes/ # Themes

{App}.Core/ # Business logic layer
├── Models/ # Domain models
├── Services/ # Business services
├── Repositories/ # Data access
└── {Domain}/ # Grouped by domain
├── Models/
└── Services/

9. XML Documentation Comments

9.1 Rules

  • All public members must have XML comments
  • Write in English (to avoid encoding issues)
  • Be concise and clear, avoid repeating the method name

9.2 Example

/// <summary>
/// Provides user management operations including CRUD and authentication.
/// </summary>
public interface IUserService
{
/// <summary>
/// Gets a user by their unique identifier.
/// </summary>
/// <param name="id">The unique identifier of the user.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>The user if found; otherwise, null.</returns>
/// <exception cref="ArgumentException">Thrown when id is empty.</exception>
Task<User?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
}

10. Record Types

Used for immutable Data Transfer Objects (DTOs):

// Request DTO
public record CreateUserRequest
{
public required string Username { get; init; }
public required string Password { get; init; }
public string DisplayName { get; init; } = string.Empty;
}

// Response DTO
public record LoginResponse
{
public required string Token { get; init; }
public required DateTime ExpiresAt { get; init; }
}

// Result type
public record AuditChainVerificationResult
{
public required bool IsValid { get; init; }
public required int TotalEntriesVerified { get; init; }
public IReadOnlyList<string> Errors { get; init; } = [];
}

11. Prohibited Practices

11.1 Naming Prohibitions

// ❌ Prohibited - Hungarian notation
string strName;
int iCount;
bool bIsActive;

// ❌ Prohibited - Abbreviations (unless commonly accepted like Id, Url, Html)
string usrNm;
int cnt;

// ❌ Prohibited - Underscore-prefixed public members
public string _username;

// ❌ Prohibited - All caps (except for acronyms)
public const int MAXRETRYCOUNT = 3;

11.2 Commonly Accepted Abbreviations

AbbreviationFull Name
IdIdentifier
UrlUniform Resource Locator
HtmlHyperText Markup Language
XmlExtensible Markup Language
JsonJavaScript Object Notation
DtoData Transfer Object
IoInput/Output
UiUser Interface
ApiApplication Programming Interface

12. Project Prefix Reference Table

PrefixPurposeExample
GST.*Base frameworkGST.Core.Common, GST.Plugin.Modbus
GST.App.*General applicationsGST.App.ProcessVision
Jope.*Jope-specific applicationsJope.SMB.Core
Leyu.*Leyu-specific applicationsLeyu.{ProjectName}
{Company}.*Other company-specificPer client requirements

13. Checklist

When creating new projects or during code review, verify the following:

Project Level

  • Project name follows GST.* or {Company}.* format
  • Test projects use .Tests suffix
  • Uses file-scoped namespaces

File Level

  • File name matches the primary class name
  • Interface files use I prefix
  • Appropriate use of Entity/Request/Response suffixes

Class Level

  • Interfaces use I prefix
  • Abstract classes use Base suffix
  • Service/Manager/Handler distinction is correct

Member Level

  • Private fields use _camelCase
  • Public properties use PascalCase
  • Async methods use Async suffix
  • Boolean properties use Is/Has/Can/Should prefix

Documentation

  • All public members have XML comments
  • Comments are in English

Version History

VersionDateChange Description
1.02025-01-17Initial creation