C# 编程指南:编码规范

在C#开发中,编码规范是一种约定,用于规范化代码的书写风格、命名规则和代码组织方式,以提高代码的可读性、可维护性和一致性。

部分
1
命名规则
类名、接口名和结构名应使用帕斯卡命名法(PascalCase),即每个单词的首字母大写,不包含下划线。
public class CustomerService
{
    // ...
}

public interface ILogger
{
    // ...
}

public struct Point3D
{
    // ...
}
方法名、属性名和事件名应使用帕斯卡命名法(PascalCase)。
public class Circle
{
    public double CalculateArea()
    {
        // ...
    }

    public double Radius { get; set; }

    public event EventHandler Clicked;
}
私有字段和局部变量应使用小写字母开头的驼峰命名法(camelCase)。
private int itemCount;
private string customerName;
常量应使用大写字母和下划线分隔的命名法(UPPER_CASE)。
public const int MAX_ITEMS = 100;
public const string DEFAULT_NAME = "John Doe";
部分
2
代码格式化和排版
使用大括号({})来定义代码块,且左大括号与语句在同一行,右大括号单独占一行。
public void DoSomething()
{
    if (condition)
    {
        // ...
    }
    else
    {
        // ...
    }
}
在二元运算符周围使用空格,增加代码的可读性。
int result = a + b;
bool isValid = x > y && z <= 100;
为了提高代码的可读性,适当使用空行分隔代码块和逻辑段落。
public class MyClass
{
    private int field1;
    private string field2;

    public void Method1()
    {
        // ...
    }

    public void Method2()
    {
        // ...
    }
}
部分
3
注释和文档
使用注释(//)或文档注释(///)来解释代码的意图、功能和使用方法。
// 计算圆的面积
public double CalculateArea(double radius)
{
    // ...
}

/// <summary>
/// 执行某个操作。
/// </summary>
/// <param name="value">输入值。</param>
public void DoSomething(int value)
{
    // ...
}
文档注释应包含方法、属性和类的摘要、参数说明和返回值说明。
/// <summary>
/// 计算圆的面积。
/// </summary>
/// <param name="radius">圆的半径。</param>
/// <returns>圆的面积。</returns>
public double CalculateArea(double radius)
{
    // ...
}
部分
4
异常处理
在可能引发异常的代码块中使用 try-catch 块来捕获和处理异常。
try
{
    // 可能引发异常的代码
}
catch (Exception ex)
{
    // 异常处理逻辑
}
在捕获异常时,尽量具体地捕获特定类型的异常,并避免捕获过于宽泛的异常类型。
try
{
    // 可能引发异常的代码
}
catch (ArgumentNullException ex)
{
    // 处理参数为空的异常
}
catch (InvalidOperationException ex)
{
    // 处理无效操作的异常
}
catch (Exception ex)
{
    // 处理其他异常
}
部分
5
类和成员的顺序
类中的成员应按照一定的顺序进行排列,通常是先字段、属性和构造函数,然后是方法和事件。
public class MyClass
{
    // 字段
    private int field1;
    private string field2;

    // 属性
    public int Property1 { get; set; }
    public string Property2 { get; set; }

    // 构造函数
    public MyClass()
    {
        // ...
    }

    // 方法
    public void Method1()
    {
        // ...
    }

    public void Method2()
    {
        // ...
    }

    // 事件
    public event EventHandler SomethingHappened;
}
部分
6
使用关键字和修饰符
对于可重写的成员,使用 virtual 关键字进行标记。
public class MyBaseClass
{
    public virtual void MyMethod()
    {
        // ...
    }
}

public class MyDerivedClass : MyBaseClass
{
    public override void MyMethod()
    {
        // ...
    }
}
对于派生类中重写的成员,使用 override 关键字进行标记。
public class MyBaseClass
{
    public virtual void MyMethod()
    {
        // ...
    }
}

public class MyDerivedClass : MyBaseClass
{
    public override void MyMethod()
    {
        // ...
    }
}
使用 const 关键字声明编译时常量。
public const int MaxValue = 100;
使用 readonly 关键字声明只读字段。
private readonly int initialValue = 10;
使用 static 关键字声明静态成员。
public static void MyStaticMethod()
{
    // ...
}
部分
7
使用空格和缩进
适当使用空格来提高代码的可读性,例如在运算符周围、方法参数之间以及控制流语句中。
int result = a + b;
if (condition)
{
    // ...
}
使用适当的缩进来表示代码块的层次结构。
public void MyMethod()
{
    if (condition1)
    {
        if (condition2)
        {
            // ...
        }
    }
    else
    {
        // ...
    }
}
部分
8
使用using指令
使用 using 指令引入命名空间,可以避免在代码中频繁使用完全限定名称。
using System;
using System.Collections.Generic;

public class MyClass
{
    public void MyMethod()
    {
        List<int> myList = new List<int>();
        // ...
    }
}
尽量避免在命名空间中使用 using 指令引入整个命名空间,而是只引入需要的类型。
using System.Collections.Generic;

public class MyClass
{
    public void MyMethod()
    {
        List<int> myList = new List<int>();
        // ...
    }
}
部分
9
字符串操作
使用 string 类型的 string.Empty 字段代替空字符串的字面量。
string myString = string.Empty;
对于字符串拼接,优先使用 string.Format 方法或插值字符串。
string name = "John";
int age = 30;

// 使用 string.Format
string message = string.Format("My name is {0} and I'm {1} years old.", name, age);

// 使用插值字符串
string message = $"My name is {name} and I'm {age} years old.";
部分
10
数组和集合
对于数组和集合的声明,将类型放在元素后面的括号中,并在声明后添加空格。
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
List<string> names = new List<string>() { "John", "Jane", "Bob" };
部分
11
异步编程
在异步方法的命名中,应使用 Async 后缀来表示该方法是异步的。
public async Task<int> CalculateSumAsync()
{
    // ...
}
在异步方法中,使用 await 关键字来等待异步操作的完成。
public async Task DoSomethingAsync()
{
    // 异步操作
    await SomeAsyncMethod();
    // ...
}
部分
12
LINQ查询
在使用 LINQ 进行查询时,使用查询表达式或方法链的方式来提高代码的可读性。
var query = from person in people
            where person.Age > 18
            select person.Name;

var query = people.Where(person => person.Age > 18)
                  .Select(person => person.Name);
对于较长或复杂的 LINQ 查询,将其拆分为多个语句或使用临时变量来提高可读性。
var adults = people.Where(person => person.Age > 18);
var names = adults.Select(person => person.Name);
部分
13
单元测试
在编写单元测试时,使用测试框架(如 NUnit、xUnit 或 MSTest)来组织和运行测试。
[TestFixture]
public class MyTestClass
{
    [Test]
    public void MyTestMethod()
    {
        // 测试逻辑
    }
}
对于单元测试方法的命名,使用描述性的名称来表达被测试方法的行为和预期结果。
[Test]
public void CalculateSum_ShouldReturnCorrectValue()
{
    // 测试逻辑
}
部分
14
异常处理
在捕获异常时,尽量精确地捕获特定类型的异常,并避免捕获通用的 Exception 类型。
try
{
    // 代码块
}
catch (SpecificException ex)
{
    // 处理特定异常
}
catch (AnotherException ex)
{
    // 处理另一种异常
}
在处理异常时,可以选择记录异常信息或进行适当的错误处理,如回滚事务或通知用户。
try
{
    // 代码块
}
catch (SpecificException ex)
{
    // 记录异常信息
    logger.Log(ex);

    // 执行适当的错误处理
    ShowErrorMessage("An error occurred. Please try again later.");
}
部分
15
文件和路径操作
在进行文件和路径操作时,使用 Path 类和 File 类提供的方法,而不是手动拼接路径和文件名。
string directoryPath = @"C:\MyDirectory";
string filePath = Path.Combine(directoryPath, "myfile.txt");

if (File.Exists(filePath))
{
    // 处理文件逻辑
}
部分
16
日志记录
在应用程序中使用适当的日志记录框架(如 Serilog、NLog 或 log4net)来记录关键信息、错误和调试信息。
logger.LogInformation("Processing started.");

try
{
    // 代码块
}
catch (Exception ex)
{
    logger.LogError(ex, "An error occurred during processing.");
}
部分
17
代码注释
使用注释来解释代码的意图、算法或特殊考虑事项。注释应该清晰、简洁并与代码保持同步。
// 计算两个整数的和
int result = a + b;
避免编写明显的注释,而注释应该提供有价值的信息。
// 下面是一个循环
for (int i = 0; i < 10; i++)
{
    // ...
}
部分
18
命名空间和文件结构
使用合适的命名空间来组织和命名相关的类型。
namespace MyProject.Utilities
{
    public class Helper
    {
        // ...
    }
}
将每个类放置在与其名称相匹配的文件中,并使用 PascalCase 进行命名。
MyClass.cs
    目录

  • 1.
    命名规则
  • 2.
    代码格式化和排版
  • 3.
    注释和文档
  • 4.
    异常处理
  • 5.
    类和成员的顺序
  • 6.
    使用关键字和修饰符
  • 7.
    使用空格和缩进
  • 8.
    使用using指令
  • 9.
    字符串操作
  • 10.
    数组和集合
  • 11.
    异步编程
  • 12.
    LINQ查询
  • 13.
    单元测试
  • 14.
    异常处理
  • 15.
    文件和路径操作
  • 16.
    日志记录
  • 17.
    代码注释
  • 18.
    命名空间和文件结构