c语言安全编码指南(编码指南和最佳实践)

本文档描述了使用C#作为语言在.NET中开发软件应用程序和类库的规则和建议。主要目的是定义通用的指导方针,以实施一致的编码风格和格式,这对开发人员避免在使用c#开发软件应用程序时经常犯的错误是有用的。本文档涵盖了命名约定、编码风格和一些体系结构级别的建议。

编码标准的目的和实现它的最佳实践

编码标准是一套用于编程语言的指导方针,它推荐编程风格和最佳实践来实现它。

编码标准通常包括缩进、注释、命名约定、编程实践、项目中的文件结构、架构最佳实践等。强烈建议软件开发人员遵循这些指导方针。编码指南有以下优点。

  • 增加编写的源代码的可读性。
  • 将包含更少的错误,工作效率更高。
  • 需要较少的维护。
  • 对于新旧开发人员来说,维护和修改代码都更容易。
  • 提高了开发人员的生产力。
  • 降低软件开发的总成本。
  • 这是一个成功的项目和一个在最坏的情况下交付失败的项目之间的区别。

命名约定

在C#编程中有三种常用的命名约定,

  • Pascal约定-所有单词的第一个字符为大写,其他字符为小写。

例如:HelloWorld

  • 驼峰格式约定——除第一个单词外,所有单词的第一个字符都是大写,其他字符都是小写。

例如:helloWorld

  • 匈牙利式约定——很久以前开发人员就使用数据类型作为前缀来定义变量。这个约定现在除了局部变量声明外,不在任何地方使用。

例如:string m_sName; string strName; int iAge;

c语言安全编码指南(编码指南和最佳实践)(1)

控制前缀命名约定

在开发WEB和窗口应用程序时,通常使用的最佳实践是为UI元素使用前缀。因为ASP.NET和Windows Form有太多可用的控件,因此可以用表格来总结它们。

c语言安全编码指南(编码指南和最佳实践)(2)

代码缩进和注释

  • 好的布局使用格式来强调代码的结构,使代码更容易阅读。要做到这一点,以下几点是有帮助的:
  • 使用Microsoft Visual Studio提供的默认代码编辑器设置。
  • 每行只写一条语句和声明。
  • 在每个方法之间添加一个空格。
  • 使用括号来理解编写的代码。
  • 使用xml注释来描述函数、类和构造函数。
  • 使用Tab来缩进。
  • 使用一个空行来分隔代码的逻辑组。
  • 按照下面的方法,使用#region和#endregion来分组相关的代码段
  1. 私有成员
  2. 私有财产
  3. 公共属性
  4. 构造函数
  5. 事件处理程序/操作方法
  6. 私有方法
  7. 公共方法
  • 不要为每一行代码和声明的每个变量都写注释。
  • 注释使用//或///避免使用/*
  • 如果您因为任何原因不得不使用一些复杂的程序,请用足够的注释很好地记录它。
  • 如果所有的变量和方法名都是有意义的,这将使代码非常易读,并且不需要很多注释。

良好的编程实践

  • 避免编写长函数。典型的函数最多应该有40-50行代码。如果方法的代码超过50行,则必须考虑将其重构为单独的私有方法。
  • 避免编写长类文件。典型的类文件应该包含600-700行代码。如果类文件有700行以上的代码,则必须创建partial类。partial类在编译后将代码组合成单个单元。
  • 不要在一个文件中有类的数量。为每个类创建一个单独的文件。
  • 避免使用var代替dynamic。
  • 在操作符周围添加空格,如 、-、==等。
  • 始终在关键字if、else、do、while、for和foreach后面加上开括号和闭括号,即使语言不需要这样做。
  • 方法名应该具有有意义的名称,以便它不会误导名称。有意义的方法名不需要代码注释。

Good:privatevoidSaveAddress(Addressaddress){} Bad: //Thismethodusedtosaveaddress privatevoidSave(Addressaddress){}

  • 方法或功能应该只有单一的职责(一个任务)。不要试图将多个功能组合为一个功能。

Good:publicvoidUpdateAddress(Addressaddress){} publicvoidInsertAddress(Addressaddress){} Bad:publicvoidSaveAddress(Addressaddress){ if(address.AddressId==0){}else{} }

  • MVC中的控制器动作应该有有意义的名字,每个动作只有一个责任。

Good:publicclassEmployeeController:Controller{ publicActionResultIndex(){} publicActionResultCreate(){} [HttpPost] publicActionResultCreate(EmployeeModelemployee){} publicActionResultEdit(intid){} [HttpPut] publicActionResultUpdate(EmployeeModelemployee){} [HttpDelete] publicJsonResultDelete(intid){} } Bad:publicclassEmployeeController:Controller{ publicActionResultGetAll(){} publicActionResultCreateEmployee(){} [HttpPost] publicActionResultCreateEmployee(EmployeeModelemployee){} publicActionResultEditEmployee(intid){} [HttpPut] publicActionResultUpdateEmployee(EmployeeModelemployee){} [HttpDelete] publicJsonResultEmployeeDelete(intid){} }

在上面的例子中,你谈论的是employee,那么不应该有带有employee前缀或扩展名的动作方法名

  • 避免使用普通类型系统。使用语言特定的别名

Good: intage; stringfirstName; objectaddressInfo; Bad: System.Int32age;StringfirstName; ObjectaddressInfo;

  • 不要硬编码字符串或数字;相反,创建单独的文件sfor常量并将所有常量放入其中,或者在文件顶部声明常量并将这些常量引用到代码中。
  • 您还可以在配置文件中以密钥和值对的形式放置一些常量,如数据库连接、记录器文件名、SMTP信息变量等。
  • 不要硬编码字符串。使用资源文件。
  • 当比较字符串时,将字符串变量转换为大写或小写

Good:if(firstName.ToLower()=="yogesh"){} if(firstName.ToUpper()==“YOGESH”){} Bad:if(firstName==“rohit”){}

  • 使用String.Empty 替代 “”

Good:if(firstName==String.Empty){} Bad:if(firstName==“”){}

  • 在需要的地方使用枚举,不要使用数字或字符串来表示离散值

Good:publicenumLoggerType{ Event, File, Database } publicvoidLogException(stringmessage,LoggerTypeloggerType){ switch(loggerType){ caseLoggerType.Event: //Dosomethingbreak; caseLoggerType.File: //Dosomethingbreak; caseLoggerType.Database: //Dosomethingbreak; default: //Dosomethingbreak; } } Bad:publicvoidLogException(stringmessage,LoggerTypeloggerType){ switch(loggerType){ case"Event": //Dosomethingbreak; case"File": //Dosomethingbreak; case"Database": //Dosomethingbreak; default: //Dosomethingbreak; } }

  • 事件处理程序不应包含执行所需操作的代码。而是从事件处理程序调用另一个私有或公共方法。保持事件处理程序或动作方法尽可能干净。
  • 不要在代码中硬编码路径或驱动器名。以编程方式获取应用程序路径并使用相对路径。使用输入或输出类System.IO)来实现这一点。
  • 在访问对象和复杂对象之前,总是对它们进行空检查。

Good:publicContactGetContactDetails(Addressaddress){ if(address!=null&&address.Contact!=null){ returnaddress.Contact; } } Bad:publicContactGetContactDetails(Addressaddress){ returnaddress.Contact; }

  • 最终使用的错误消息应该是用户友好和自解释的,但使用logger记录实际的异常细节。为此创建常量并在应用程序中使用它们。

Good: “Erroroccurredwhileconnectingtodatabase.Pleasecontactadministrator.”“Yoursessionhasbeenexpired.Pleaseloginagain.” Bad: “ErrorinApplication.” “Thereisanerrorinapplication.”

  • 避免公开公共方法和属性,除非确实需要从类外部访问它们。如果仅在同一个程序集中访问,则使用internal;如果在同一个类中使用,则使用private。
  • 避免向函数传递太多参数。如果你有超过4-5个参数,使用类或结构来传递它。

Good:publicvoidUpdateAddress(Addressaddress){} Bad:publicvoidUpdateAddress(intaddressId,stringcountry,stringstate,stringphoneNumber,stringpinCode,stringaddress1,stringaddress2){}

  • 在使用collection时,请注意以下几点:
  1. 返回集合时,如果没有要返回的数据,则返回空集合,而不是返回null。
  2. 总是检查Any()运算符,而不是检查count,例如:collection.Count > 0和空值检查
  3. 遍历时使用foreach而不是for循环。
  4. 使用IList<T>、IEnumerable<T>、ICollection<T>而不是具体的类,例如使用List<>
  • 使用对象初始化器来简化对象创建

Good:varemployee=newEmployee{ FirstName=“ABC”,LastName=“PQR”,Manager=“XYZ”,Salary=12346.25 }; Bad:varemployee=newEmployee(); employee.FirstName=“ABC”; employee.LastName=“PQR”; employee.Manager=“XYZ”; employee.Salary=12346.25;

  • 使用语句应该先按框架名称空间排序,然后按升序排序应用程序名称空间

usingSystem; usingSystem.Collections.Generic;usingSystem.IO; usingSystem.Text; usingCompany.Product.BusinessLayer;

  • 如果你正在打开数据库连接、套接字、文件流等,总是在最后一个块中关闭它们。这将确保即使在打开连接后发生异常,该连接也将在finally块中安全关闭。
  • 使用c# using语句简化代码。如果您有一个try-finally语句,其中finally块中唯一的代码是对Dispose方法的调用,则使用using语句代替。

Good:using(varfileToOpen=newFileInfo(fileName)){ //Fileoperation } Bad:varfileInfo=newFileInfo(fileName); try{ //Fileoperation }finally{ if(fileInfo!=null){ fileInfo.Delete(); } }

  • 始终只捕获特定异常,而不是捕获泛型异常。

voidReadFile(stringfileName){ try{ //readfromfile. }catch(System.IO.IOExceptionfileException){ //logtheerror.Re-throwexceptionthrowfileException; }finally{} } Bad:voidReadFile(stringfileName){ try{ //readfromfile. }catch(Exceptionex){ //catchinggeneralexception }finally{} }

  • 当必须在循环中操作字符串对象时,使用StringBuilder类而不是String。String对象在.NET中的工作方式很奇怪。每次添加字符串时,它实际上是丢弃旧的字符串对象并重新创建一个新对象,这是一个相对昂贵的操作。

架构设计级别指南

  • 始终使用多层(N-Tier)架构。
  • 使用接口和抽象类实现松散耦合的体系结构。
  • 使用泛型可以帮助您创建可重用的类和函数

publicclassMyClass<T>whereT:SomeOtherClass{ publicvoidSomeMethod(Tt){ SomeOtherClassobj=t; } }

  • 将应用程序分离为多个程序集。为UI、业务层、数据访问层、框架、异常处理和日志组件创建单独的组件。
  • 不要从UI页面访问数据库。使用数据访问层来执行所有与数据库相关的任务。
  • 总是使用存储过程而不是在c#代码中编写内联查询。
  • 在创建、更新和删除等数据库操作中始终使用事务。这将有助于在Sql语句执行过程中发生任何异常时再次回滚旧数据。
  • 不要将复杂的逻辑放入存储过程中,而是将其放入业务层。
  • 类似地,所有系统级的过程都以“sp”开头,函数以“fn”开头,这将触发重载来搜索过程。
  • 为了在客户端机器上安装数据库,用户安装程序sql脚本。
  • 尝试使用设计模式、实践和坚实的原则。
  • 对于相同的代码,创建单独的实用程序文件或将其移动到基类中。
  • 在数据层中使用try-catch-finally来捕获所有数据库异常。这个异常处理程序应该记录数据库中的所有异常。记录的详细信息应该包括正在执行的命令的名称、存储的proc名称、参数、使用的连接字符串等。在记录异常之后,它可以被重新抛出到调用者层,这样应用程序就可以捕获它并在UI上显示特定于用户的消息
  • 不要将大型对象存储到会话中。将大型或复杂对象存储到会话中可能会消耗服务器内存。在使用后销毁或处置这些会话变量。
  • 不要将大型对象存储到视图状态,这会增加页面加载时间。
  • 请始终通过NuGet包引用第三方dll、javascripts和css框架,以便随时更新最新版本。
  • 总是引用缩小版的javascript或css文件,这将减少服务器不必要的开销。
,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。