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






3.50/5 (123投票s)
2004年12月5日
7分钟阅读

460812

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 命名法。使用有意义、描述性的词语来命名变量
- 请勿使用缩写。使用
name
、address
、salary
等,而不是nam
、addr
、sal
。 - 请勿使用单字符变量名,如
i
、n
、x
等。请使用index
和temp
等名称。
此处的唯一例外是循环中用于迭代的变量
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;
}
类中的每个方法之间应仅有一个空行。花括号应在新行上,而不是与 if
、for
等在同一行。
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;
}
}
请勿将成员变量设为 public
或 protected
。将其保持为 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
继承。