65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 编码标准和最佳编程实践

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.50/5 (123投票s)

2004年12月5日

7分钟阅读

viewsIcon

460812

downloadIcon

2

任何人都可以编写代码!通过几个月的编程经验,您就可以编写“可运行的应用程序”。让它运行起来很容易,但以最高效的方式实现则需要更多的工作!

引言

请相信:大多数程序员编写的是“能运行的代码”,而不是“高效的代码”。正如我们在本教程开头提到的,您想成为公司中“最有价值的专业人士”吗?编写“高效的代码”是一门艺术,您必须学会并加以实践。

命名约定和标准

  • Pascal 命名法:所有单词的首字母大写,其余字母小写。
  • Camel 命名法:除了第一个单词外,所有单词的首字母大写,其余字母小写。

类名使用 Pascal 命名法

public class HelloWorld
{
  ...
}

方法名使用 Pascal 命名法

public class HelloWorld
{
  void SayHello(string name)
  {
    ...
  }
}

变量和方法参数使用 Camel 命名法

public class HelloWorld
{
 int totalCount = 0;
 void SayHello(string name)
 {
  string fullMessage = "Hello " + name;
  ...
 }
}

请勿使用匈牙利命名法为变量命名。早期,大多数程序员喜欢它:将数据类型作为变量名的前缀,并使用 m_ 作为成员变量的前缀,例如

string m_sName;
int nAge;

然而,在 .NET 编码标准中,不推荐这样做。不应使用数据类型和 M_ 来表示成员变量。所有变量都应使用 Camel 命名法。使用有意义、描述性的词语来命名变量

  • 请勿使用缩写。使用 nameaddresssalary 等,而不是 namaddrsal
  • 请勿使用单字符变量名,如 inx 等。请使用 indextemp 等名称。

此处的唯一例外是循环中用于迭代的变量

for ( int i = 0; i < count; i++ )
{
 ...
}

如果变量仅用作迭代计数器,并且在循环中的其他地方不使用,许多人仍然喜欢使用单字符变量(i),而不是 invent 一个不同的合适名称。

  • 请勿在变量名中使用下划线 (_)。
  • 命名空间名称应遵循标准模式。
<company name>.<product name>.<top level module>.<bottom level module>

文件名应与类名匹配。例如,对于类 HelloWorld,文件名应为 helloworld.cs(或 helloworld.vb)。

缩进和间距:使用 TAB 进行缩进。请勿使用空格。

注释应与代码在同一级别。花括号 ({}) 应与花括号外的代码在同一级别。使用一个空行分隔代码的逻辑组。

bool SayHello (string name)
{
  string fullMessage = "Hello " + name;
  DateTime currentTime = DateTime.Now;
  string message = fullMessage + ", the time is : " + 
                  currentTime.ToShortTimeString();
  MessageBox.Show ( message );
  if ( ... )
  {
  // Do something
  // ...
  return false;
  }
  return true;
}

此代码看起来比上面的代码更好

bool SayHello ( string name )
{
  string fullMessage = "Hello " + name;
  DateTime currentTime = DateTime.Now;
    string message = fullMessage + ", the time is : " + 
    currentTime.ToShortTimeString();
  MessageBox.Show ( message );
  if ( ... )
  {
    // Do something
    // ...
   return false;
 }

  return true;
}

类中的每个方法之间应仅有一个空行。花括号应在新行上,而不是与 iffor 等在同一行。

Good

  if ( ... ) 
  {
   // Do something
  }

不好的

  if ( ... ) {
   // Do something
  }

在每个运算符和括号前后使用一个空格。

Good

  if ( showResult == true )
  {
   for ( int i = 0; i < 10; i++ )
   {
    //
   }
  }

不好的

  if(showResult==true)
  {
   for(int i= 0;i<10;i++)
   {
    //
   }
  }

良好的编程实践

避免文件过大。如果一个文件包含超过 300-400 行代码,您必须考虑将代码重构为辅助类。避免编写非常长的方法。一个方法通常应有 1-25 行代码。如果一个方法超过 25 行代码,您必须考虑将其重构为单独的方法。方法名应能说明其功能。请勿使用误导性的名称。如果方法名显而易见,则无需文档说明该方法的作用。

Good

void SavePhoneNumber ( string phoneNumber )
{
  // Save the phone number.
}

不好的

 // This method will save the phone number.
 void SaveData ( string phoneNumber )
 {
  // Save the phone number.
 }

一个方法只应做“一件事”。请勿在一个方法中组合多个任务,即使这些任务非常小。

Good

 // Save the address.
 SaveAddress (  address );
 
 // Send an email to the supervisor to inform that the address is updated.
 SendEmail ( address, email );  
 
 void SaveAddress ( string address )
 {
  // Save the address.
  // ...
 }
 
 void SendEmail ( string address, string email )
 {
  // Send an email to inform the supervisor that the address is changed.
  // ...
 }

不好的

 // Save address and send an email to the supervisor
 // to inform that the address is updated.
 SaveAddress ( address, email );
 void SaveAddress ( string address, string email )
 {
  // Job 1.
  // Save the address.
  // ...
 // Job 2.
  // Send an email to inform the supervisor that the address is changed.
  // ...
 }

使用 C# 或 VB.NET 特定的类型,而不是 System 命名空间中定义的别名类型。

Good

 int age;
 string name;
 object contactInfo;

不好的

 Int16 age;
 String name;
 Object contactInfo;

请勿硬编码数字。改用常量。请勿硬编码字符串。使用资源文件。避免使用过多的成员变量。声明局部变量并将其传递给方法,而不是在方法之间共享成员变量。如果在方法之间共享成员变量,将难以跟踪哪个方法何时更改了该值。在需要时使用 enum。请勿使用数字或字符串来表示离散值。

Good

enum MailType
 {
  Html,
  PlainText,
  Attachment
 }
 void SendMail (string message, MailType mailType)
 {
  switch ( mailType )
  {
   case MailType.Html:
    // Do something
    break;
   case MailType.PlainText:
    // Do something
    break;
   case MailType.Attachment:
    // Do something
    break;
   default:
    // Do something
    break;
  }
 }

不好的

void SendMail (string message, string mailType)
{
  switch ( mailType )
  {
   case "Html":
    // Do something
    break;
   case "PlainText":
    // Do something
    break;
   case "Attachment":
    // Do something
    break;
   default:
    // Do something
    break;
  }
}

请勿将成员变量设为 publicprotected。将其保持为 private,并公开 public/protected 属性。切勿在代码中硬编码路径或驱动器名称。以编程方式获取应用程序路径并使用相对路径。切勿假设您的代码将从 C: 驱动器运行。您永远不知道;某些用户可能从网络或 Z: 驱动器运行它。

在应用程序启动时,执行某种“自检”,并确保所有必需的文件和依赖项都位于预期的位置。如果需要,请在启动时检查数据库连接。在出现任何问题时,向用户显示友好的消息。

如果找不到所需的配置文件,应用程序应能够创建一个具有默认值的配置文件。如果配置文件中发现错误的值,应用程序应抛出错误或显示消息,并告知用户正确的值是多少。

错误消息应帮助用户解决问题。切勿显示“应用程序错误”、“出现错误”等错误消息。相反,应显示具体消息,如“数据库更新失败。请确保登录 ID 和密码正确。”

显示错误消息时,除了告知问题所在,还应告知用户如何解决问题。而不是显示“数据库更新失败”之类的消息,建议用户应执行的操作:“数据库更新失败。请确保登录 ID 和密码正确。”

向用户显示简短而友好的消息,但记录实际错误以及所有可能的信息。这将极大地有助于诊断问题。

注释

不要为每一行代码和声明的每个变量编写注释。在需要的地方编写注释。良好、可读的代码将需要很少的注释。如果所有变量名和方法名都有意义,这将使代码非常易读,并且不需要太多注释。较少的注释行将使代码更优雅。但是,如果代码不干净/可读且注释很少,那会更糟糕。如果您出于任何原因必须使用一些复杂或奇怪的逻辑,请用足够的注释对其进行很好的文档化。如果您将一个数值变量初始化为 0、-1 等以外的特殊数字,请记录选择该值的原因。底线是:编写干净、可读的代码,使其无需任何注释即可理解。对注释进行拼写检查,并确保使用正确的语法和标点符号。

异常处理

切勿执行“捕获异常但不执行任何操作”。如果您隐藏了异常,您将永远不知道异常是否发生。在异常情况下,向用户显示友好的消息,但记录实际错误以及关于错误的尽可能多的详细信息,包括发生时间、方法和类名等。始终仅捕获特定的异常,而不是通用异常。

Good

void ReadFromFile ( string fileName )
 {
  try
  {
   // read from file.
  }
  catch (FileIOException ex)
  {
   // log error.
   //  re-throw exception depending on your case.
   throw;
  }
 }

不好的

void ReadFromFile ( string fileName )
{
  try
  {
    // read from file.
  }
  catch (Exception ex)
  {
    // Catching general exception is bad... we will never know whether it
    // was a file error or some other error.

    // Here you are hiding an exception.
    // In this case no one will ever know that an exception happened.
    return "";
  }
}

您不需要在所有方法中捕获通用异常。将其保持开放状态,让应用程序崩溃。这将帮助您在开发周期中发现大多数错误。您可以有一个应用程序级别(线程级别)的错误处理程序,您可以在其中处理所有通用异常。在发生“意外通用错误”的情况下,此错误处理程序应捕获异常,并在向用户显示友好消息后,记录错误,然后再关闭应用程序或允许用户“忽略并继续”。

不要在所有方法中编写 try-catch。仅在可能发生特定异常时使用。例如,如果您正在写入文件,请仅处理 FileIOException

请勿编写非常大的 try-catch 块。如果需要,请为执行的每个任务编写单独的 try-catch,并仅将特定代码段包含在 try-catch 中。这将帮助您找到生成异常的代码段,并可以向用户提供具体的错误消息。

如果需要,您可以在应用程序中编写自己的自定义异常类。不要让您的自定义异常继承自基类 SystemException。而是从 ApplicationException 继承。

© . All rights reserved.