csharp-code-style

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

C# Code Style Guide

C#代码风格指南

Overview

概述

C# 코딩 표준 (POCU 기반, C# 9.0 기준)
Core Topics:
  • 명명 규칙 (m접두어, b접두어, E/S접두어)
  • 코드 작성 규칙
  • 클래스 구조 순서
  • C# 9.0 패턴
C#编码标准(基于POCU,以C# 9.0为基准)
核心主题:
  • 命名规则(m前缀、b前缀、E/S前缀)
  • 代码编写规则
  • 类结构顺序
  • C# 9.0模式

Naming Conventions

命名规范

Quick Reference

快速参考

ElementConventionExample
ClassPascalCase
PlayerManager
,
OrderService
StructSPascalCase
SUserID
,
SPlayerData
InterfaceIPascalCase
IDisposable
,
IOrderService
EnumEPascalCase
EDirection
,
EOrderStatus
Method (public)PascalCase (동사+명사)
GetAge()
,
ProcessOrder()
Method (private)camelCase
getAge()
,
processOrder()
PropertyPascalCase
Name
,
OrderID
Private FieldmPascalCase
mAge
,
mOrderRepository
Local VariablecamelCase
totalAmount
,
isValid
ParametercamelCase
customerId
,
orderDate
ConstantALL_CAPS
MAX_RETRY_COUNT
,
DEFAULT_TIMEOUT
Static readonlyALL_CAPS
MY_CONST_OBJECT
Boolean VariablebCamelCase / mbPascalCase
bFired
,
mbIsEnabled
Boolean PropertyIs/Has/Can/Should
IsFired
,
HasChildren
元素规范示例
PascalCase
PlayerManager
,
OrderService
结构体SPascalCase
SUserID
,
SPlayerData
接口IPascalCase
IDisposable
,
IOrderService
枚举EPascalCase
EDirection
,
EOrderStatus
方法(公共)PascalCase(动词+名词)
GetAge()
,
ProcessOrder()
方法(私有)camelCase
getAge()
,
processOrder()
属性PascalCase
Name
,
OrderID
私有字段mPascalCase
mAge
,
mOrderRepository
局部变量camelCase
totalAmount
,
isValid
参数camelCase
customerId
,
orderDate
常量ALL_CAPS
MAX_RETRY_COUNT
,
DEFAULT_TIMEOUT
静态只读ALL_CAPS
MY_CONST_OBJECT
布尔变量bCamelCase / mbPascalCase
bFired
,
mbIsEnabled
布尔属性Is/Has/Can/Should
IsFired
,
HasChildren

Private Member Pattern

私有成员模式

csharp
public class OrderService
{
    // Private fields: m + PascalCase
    private readonly IOrderRepository mOrderRepository;
    private readonly ILogger mLogger;
    private int mRetryCount;
    private bool mbIsProcessing;  // boolean: mb prefix

    // Constructor parameter: camelCase (no prefix)
    public OrderService(IOrderRepository orderRepository, ILogger logger)
    {
        mOrderRepository = orderRepository;
        mLogger = logger;
    }

    // Public method: PascalCase
    public Order GetOrder(int orderId)
    {
        return getOrderInternal(orderId);
    }

    // Private method: camelCase
    private Order getOrderInternal(int orderId)
    {
        return mOrderRepository.Find(orderId);
    }
}
csharp
public class OrderService
{
    // 私有字段: m + PascalCase
    private readonly IOrderRepository mOrderRepository;
    private readonly ILogger mLogger;
    private int mRetryCount;
    private bool mbIsProcessing;  // 布尔类型: mb前缀

    // 构造函数参数: camelCase(无前缀)
    public OrderService(IOrderRepository orderRepository, ILogger logger)
    {
        mOrderRepository = orderRepository;
        mLogger = logger;
    }

    // 公共方法: PascalCase
    public Order GetOrder(int orderId)
    {
        return getOrderInternal(orderId);
    }

    // 私有方法: camelCase
    private Order getOrderInternal(int orderId)
    {
        return mOrderRepository.Find(orderId);
    }
}

Enum and Struct Prefixes

枚举与结构体前缀

csharp
// Enum: E prefix
public enum EDirection
{
    None,
    North,
    South,
    East,
    West
}

// Bit flags enum: Flags suffix
[Flags]
public enum EVisibilityFlags
{
    None = 0,
    Visible = 1,
    Hidden = 2,
    Collapsed = 4
}

// Struct: S prefix (readonly struct는 제외 가능)
public struct SUserID
{
    public int Value { get; }
}

// readonly record struct: S prefix 불필요
public readonly record struct UserID(int Value);
csharp
// 枚举: E前缀
public enum EDirection
{
    None,
    North,
    South,
    East,
    West
}

// 位标志枚举: Flags后缀
[Flags]
public enum EVisibilityFlags
{
    None = 0,
    Visible = 1,
    Hidden = 2,
    Collapsed = 4
}

// 结构体: S前缀(readonly struct可省略)
public struct SUserID
{
    public int Value { get; }
}

// readonly record struct: 无需S前缀
public readonly record struct UserID(int Value);

Nullable Naming

可空类型命名

csharp
// Nullable parameter: OrNull suffix
public Animation GetAnimation(string nameOrNull)
{
    if (nameOrNull == null)
    {
        return DefaultAnimation;
    }
    return mAnimations[nameOrNull];
}

// Nullable return: OrNull suffix
public string GetNameOrNull()
{
    return mbHasName ? mName : null;
}

// Recursive function: Recursive suffix
public int FibonacciRecursive(int n)
{
    if (n <= 1)
    {
        return n;
    }
    return FibonacciRecursive(n - 1) + FibonacciRecursive(n - 2);
}
csharp
// 可空参数: OrNull后缀
public Animation GetAnimation(string nameOrNull)
{
    if (nameOrNull == null)
    {
        return DefaultAnimation;
    }
    return mAnimations[nameOrNull];
}

// 可空返回值: OrNull后缀
public string GetNameOrNull()
{
    return mbHasName ? mName : null;
}

// 递归函数: Recursive后缀
public int FibonacciRecursive(int n)
{
    if (n <= 1)
    {
        return n;
    }
    return FibonacciRecursive(n - 1) + FibonacciRecursive(n - 2);
}

Code Writing Rules

代码编写规则

Prohibited Patterns

禁止模式

csharp
// [WRONG] var keyword
var order = GetOrder(1);
var items = new List<Item>();

// [CORRECT] Explicit type
Order order = GetOrder(1);
List<Item> items = new List<Item>();

// [WRONG] Null coalescing operator (??)
string name = inputName ?? "Default";

// [CORRECT] Explicit null check
string name;
if (inputName != null)
{
    name = inputName;
}
else
{
    name = "Default";
}

// [WRONG] using declaration (C# 8.0)
using FileStream stream = new FileStream(path, FileMode.Open);

// [CORRECT] using statement
using (FileStream stream = new FileStream(path, FileMode.Open))
{
    // ...
}

// [WRONG] target-typed new()
List<Order> orders = new();

// [CORRECT] Explicit type
List<Order> orders = new List<Order>();

// [WRONG] Async suffix
public async Task<Order> GetOrderAsync(int id);

// [CORRECT] No Async suffix
public async Task<Order> GetOrder(int id);

// [WRONG] inline out declaration
if (int.TryParse(input, out int result))

// [CORRECT] separate out declaration
int result;
if (int.TryParse(input, out result))
csharp
// [错误] var关键字
var order = GetOrder(1);
var items = new List<Item>();

// [正确] 显式类型
Order order = GetOrder(1);
List<Item> items = new List<Item>();

// [错误] 空合并运算符(??)
string name = inputName ?? "Default";

// [正确] 显式空检查
string name;
if (inputName != null)
{
    name = inputName;
}
else
{
    name = "Default";
}

// [错误] using声明(C# 8.0)
using FileStream stream = new FileStream(path, FileMode.Open);

// [正确] using语句
using (FileStream stream = new FileStream(path, FileMode.Open))
{
    // ...
}

// [错误] 目标类型new()
List<Order> orders = new();

// [正确] 显式类型
List<Order> orders = new List<Order>();

// [错误] Async后缀
public async Task<Order> GetOrderAsync(int id);

// [正确] 无Async后缀
public async Task<Order> GetOrder(int id);

// [错误] 内联out声明
if (int.TryParse(input, out int result))

// [正确] 单独out声明
int result;
if (int.TryParse(input, out result))

Required Patterns

强制模式

csharp
// Always use braces, even for single line
if (condition)
{
    DoSomething();
}

// Float literals with f suffix
float value = 0.5f;
float another = 1.0f;

// Switch must have default case
switch (status)
{
    case EStatus.Active:
        Process();
        break;
    case EStatus.Inactive:
        Skip();
        break;
    default:
        Debug.Fail("Unknown status");
        break;
}

// Debug.Assert for all assumptions
Debug.Assert(order != null, "Order should not be null");
Debug.Assert(count > 0, "Count must be positive");

// Properties instead of getter/setter methods
// [WRONG]
public int GetAge() { return mAge; }
public void SetAge(int age) { mAge = age; }

// [CORRECT] (.NET 5+ only - NOT available in Unity)
public int Age { get; private init; }

// [CORRECT] (Unity compatible)
public int Age { get; private set; }
csharp
// 即使单行也要使用大括号
if (condition)
{
    DoSomething();
}

// 浮点字面量添加f后缀
float value = 0.5f;
float another = 1.0f;

// Switch语句必须包含default分支
switch (status)
{
    case EStatus.Active:
        Process();
        break;
    case EStatus.Inactive:
        Skip();
        break;
    default:
        Debug.Fail("Unknown status");
        break;
}

// 所有假设都使用Debug.Assert
Debug.Assert(order != null, "Order should not be null");
Debug.Assert(count > 0, "Count must be positive");

// 使用属性而非getter/setter方法
// [错误]
public int GetAge() { return mAge; }
public void SetAge(int age) { mAge = age; }

// [正确](仅.NET 5+可用 - Unity不支持)
public int Age { get; private init; }

// [正确](兼容Unity)
public int Age { get; private set; }

C# 9.0 Patterns

C# 9.0模式

Unity Limitation:
init
accessor is NOT available in Unity (requires .NET 5+ runtime). See Modern Patterns Reference for details and alternatives.
csharp
// private init (C# 9.0) - .NET 5+ only, NOT available in Unity
public class Customer
{
    public string Name { get; private init; }  // Use 'private set' in Unity
    public string Email { get; private init; }
}

// Record for immutable data
public record OrderDto(int Id, string CustomerName, decimal TotalAmount);

// Pattern matching switch expression (available in Unity)
public string GetStatusMessage(EOrderStatus status)
{
    return status switch
    {
        EOrderStatus.Pending => "Order is pending",
        EOrderStatus.Completed => "Order completed",
        _ => throw new ArgumentOutOfRangeException(nameof(status))
    };
}
Unity限制:
init
访问器在Unity中不可用(需要.NET 5+运行时)。 详情及替代方案请参考现代模式参考
csharp
// private init(C# 9.0) - 仅.NET 5+可用,Unity不支持
public class Customer
{
    public string Name { get; private init; }  // 在Unity中使用'private set'
    public string Email { get; private init; }
}

// 用于不可变数据的Record
public record OrderDto(int Id, string CustomerName, decimal TotalAmount);

// 模式匹配switch表达式(Unity支持)
public string GetStatusMessage(EOrderStatus status)
{
    return status switch
    {
        EOrderStatus.Pending => "Order is pending",
        EOrderStatus.Completed => "Order completed",
        _ => throw new ArgumentOutOfRangeException(nameof(status))
    };
}

Class Structure Order

类结构顺序

csharp
public class OrderService : IOrderService
{
    // 1. Constants
    private const int MAX_RETRY_COUNT = 3;
    public static readonly TimeSpan DEFAULT_TIMEOUT = TimeSpan.FromSeconds(30);

    // 2. Private member variables
    private readonly IOrderRepository mOrderRepository;
    private readonly ILogger mLogger;
    private int mProcessedCount;
    private bool mbIsInitialized;

    // 3. Properties (with private member above if needed)
    public int ProcessedCount => mProcessedCount;
    public bool IsInitialized => mbIsInitialized;

    // 4. Constructors
    public OrderService(IOrderRepository orderRepository, ILogger logger)
    {
        mOrderRepository = orderRepository;
        mLogger = logger;
    }

    // 5. Public methods
    public Order GetOrder(int id)
    {
        Debug.Assert(id > 0, "ID must be positive");
        return mOrderRepository.Find(id);
    }

    public void ProcessOrder(Order order)
    {
        validateOrder(order);
        processInternal(order);
    }

    // 6. Private methods
    private void validateOrder(Order order)
    {
        Debug.Assert(order != null);
    }

    private void processInternal(Order order)
    {
        // Implementation
    }
}
csharp
public class OrderService : IOrderService
{
    // 1. 常量
    private const int MAX_RETRY_COUNT = 3;
    public static readonly TimeSpan DEFAULT_TIMEOUT = TimeSpan.FromSeconds(30);

    // 2. 私有成员变量
    private readonly IOrderRepository mOrderRepository;
    private readonly ILogger mLogger;
    private int mProcessedCount;
    private bool mbIsInitialized;

    // 3. 属性(如需对应私有成员则放在其上方)
    public int ProcessedCount => mProcessedCount;
    public bool IsInitialized => mbIsInitialized;

    // 4. 构造函数
    public OrderService(IOrderRepository orderRepository, ILogger logger)
    {
        mOrderRepository = orderRepository;
        mLogger = logger;
    }

    // 5. 公共方法
    public Order GetOrder(int id)
    {
        Debug.Assert(id > 0, "ID must be positive");
        return mOrderRepository.Find(id);
    }

    public void ProcessOrder(Order order)
    {
        validateOrder(order);
        processInternal(order);
    }

    // 6. 私有方法
    private void validateOrder(Order order)
    {
        Debug.Assert(order != null);
    }

    private void processInternal(Order order)
    {
        // 实现逻辑
    }
}

File Organization

文件组织

  • 각 클래스는 독립된 파일에 작성
  • 파일명 = 클래스명 (정확히 일치)
  • Partial 클래스:
    ClassName.SubName.cs
  • 每个类独立编写在单独文件中
  • 文件名=类名(完全一致)
  • 分部类:
    ClassName.SubName.cs

Reference Documentation

参考文档

Naming Conventions

命名规范

Complete naming rules:
  • m/mb 접두어 상세
  • E/S 접두어 규칙
  • OrNull 접미어 패턴
  • ALL_CAPS 상수 규칙
完整命名规则:
  • m/mb前缀详情
  • E/S前缀规则
  • OrNull后缀模式
  • ALL_CAPS常量规则

Modern Patterns

现代模式

C# 9.0 language features:
  • Records and init-only properties
  • Pattern matching
  • File-scoped namespaces (C# 10)
  • 금지 패턴 상세
C# 9.0语言特性:
  • Record与init-only属性
  • 模式匹配
  • 文件作用域命名空间(C# 10)
  • 禁止模式详情

Error Handling

错误处理

Error handling patterns:
  • Debug.Assert 사용
  • 경계에서만 예외 처리
  • null 반환/매개변수 회피
  • 유효성 검증 패턴
错误处理模式:
  • Debug.Assert使用
  • 仅在边界处理异常
  • 避免空值返回/参数
  • 验证模式

Key Principles

核心原则

  1. 가독성 최우선: 명확하고 이해하기 쉬운 코드 작성
  2. 명시적 타입:
    var
    사용 금지, 타입 명시
  3. Null 안전: OrNull 접미어로 nullable 명시
  4. Assertion: 모든 가정에 Debug.Assert 사용
  5. 경계 검증: 외부 데이터는 경계에서만 검증
  6. Use init: Prefer C# 9.0 private init (.NET 5+ only, NOT available in Unity)
  7. No Emoji: 코드 예제 및 문서에서 이모지 사용 금지, 텍스트 태그 사용 ([WRONG], [CORRECT], [CAUTION])
  1. 可读性优先: 编写清晰易懂的代码
  2. 显式类型: 禁止使用
    var
    ,必须显式声明类型
  3. 空值安全: 使用OrNull后缀明确标记可空类型
  4. 断言验证: 所有假设都使用Debug.Assert
  5. 边界验证: 仅在边界处验证外部数据
  6. 使用init: 优先使用C# 9.0的private init(仅.NET 5+可用,Unity不支持)
  7. 禁止表情符号: 禁止在代码示例及文档中使用表情符号,使用文本标签([WRONG]、[CORRECT]、[CAUTION])