C++代码规范(必须要遵循以及举例)

版权、文件声明规范

 

Q:为什么要申明版权以及说明?

      这个更多意义在于以后的维护,以及代码阶段的版本控制。当项目进入成熟阶段之后,后期维护工作会占据很大的精力

      考虑到不同客户类型的需求,往往“类似功能”模块会有好几个,有些模块也不经常改动,因此很容易忘记,良好的习惯,就是增加对应的注释。

1、版权和版本的声明

【规范1-1-1】 C/C++在头文件(.h)需进行版权、版本、作者等声明。

版权和版本的声明位于头文件和源文件的开头(参见示例),主要内容有:

  1. 版权信息
  2. 文件名称,摘要
  3. 当前版本号,作者,日期
/**
*****************************************************************************
*  Copyright (C), 2010-2019, GOSUN CL
*
*  @file    文件名(包含后缀名)
*  @brief   简要描述本文件的内容
*
*  @author  作者
*  @date    输入日期
*  @version 版本号
*
*  (若本次是第一个定型版本,可以省略下列Note内容)
*
*----------------------------------------------------------------------------
*  @note 历史版本  修改人员    修改日期    修改内容
*  @note
*
*****************************************************************************
*/


///以下是例子,供参考///

/**
*****************************************************************************
*  Copyright (C), 2010-2019, GOSUN CL
*
*  @file    CLUtility.h
*  @brief   各种通用小功能/通用文件操作
*           1. 小功能:新增 正则匹配/查找下一个字符/文件夹(全部)创建
*           2. 重新封装: CECDateTime/CECString/CECFile/CECLogFile/
*           3. 新增功能模块:CECRunLog(运行日志)/CECReadIni(ini文件读取)
*
*  @author  gu
*  @date    2019-07-18
*  @version V1.1.0 20190718
*----------------------------------------------------------------------------
*  @note 历史版本  修改人员    修改日期    修改内容
*  @note v1.0      gu          2018-2-15   1.创建
*
*****************************************************************************
*/

 

2、函数声明

【规范1-2-1】 每个函数(除了构造跟析构可以不用),都应该在函数上方进行注释,注释内容要与实际内容含义保持一致。

PS:如果函数功能或输入参数等信息发生变化,需要及时修改注释信息

/**
*****************************************************************************
*  @brief   描述该函数主要功能
*
*  @param[in]     参数名(与函数里的参数一致)  参数的描述    in:表示输入类型
*  @param[out]    参数名(与函数里的参数一致)  参数的描述    out:表示输出类型
*  @param[in,out] 参数名(与函数里的参数一致)  参数的描述    in,out:表示既是输入又是输出
*
*  @return  返回值的说明
*  @author  (若与文件申明作者是不一样的,需要填写;否则,可以省略此项)  
*
*  (若本次是第一个定型版本,可以省略下列Note内容)
*----------------------------------------------------------------------------
*  @note 历史版本  修改人员    修改日期    修改内容
*  @note v1.0      gu          2019-5-15   1.创建
*
*****************************************************************************
*/


///以下是例子,供参考///
/**
*****************************************************************************
*  @brief   对外接口,用于new创建对象(实例CRunLog)
*
*  @param[in]    strPath    存放路径(完整)
*  @param[in]    nMaxDay    最多存放多少天
*  @param[in]    nLenDay    每天多少长度(字节)
*
*  @return  CRunLog的实例,指针   
*----------------------------------------------------------------------------
*  @note 历史版本  修改人员    修改日期    修改内容
*  @note v1.0      gu          2019-5-15   1.创建
*
*****************************************************************************
*/

 

 

注释规范

      Q:为什么要注释规范?

      首先,统一的注释风格,代码看起来也非常清爽,后期代码走读的时候,也更关注逻辑本身。

      其次,使用doxygen等第三方工具导出说明文档,也是需要规范的注释,降低后期的工作量。

1、代码注释

注释采用上方以及右侧两种方式。
注释应当准确、易懂,防止注释有二义性。

如果是右侧方式,建议 采用“///< xxxx”方式(在doxygen “//” 表示下一行注释)

采用“///<”进行右侧注释地方:
1、类成员(包含 成员变量、成员函数)
2、结构体(变量)
3、全局变量
4、静态变量

举例:右侧方式,注释

u_char   m_byPipe;       ///< 通道号

举例:上方方式,注释

//报文协议处理函数

void    DealWithData(const byte *pBuf, int nLen);

【规范2-1-1】 必须要进行注释的地方如下所示:

  1. 说明性文件(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部以及源文件头部应进行注释。
  2. 函数头部应进行注释。
  3. 全局变量、成员变量、静态变量、常量要有的注释。

PS:其他非“必须”注释的地方不是说不用注释。注释对于代码维护尤其重要,良好的注释能够体现一个软件的水平。

【规范2-1-2】 修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。

 

命名规范

     Q:为什么要命名风格要统一?

     命名就好比给你自己小孩取名字。小孩取名字,我们要关注 “姓”,“辈分”,“含义”,“男女偏好”,“简洁”,“好记,好理解”等等。

     所以取名字也是非常讲究的。(灵魂拷问:你爱你的孩子吗?你爱你的代码吗?既然爱,怎么不取个好点的名字呢!)

     有些人会喜欢很长的信息来表示名字,动不动就几十个字符,这样也不好,你想,如果你小孩的名字也很长,你会喜欢吗?

命名分为 类(结构体)、函数、常量、全局变量、静态变量、成员变量、临时变量(参数)等命名规则,应遵循以下规则:

两个基本原则:

  1. 含义清晰,不易混淆;
  2. 不和其它模块、系统API的命名空间相冲突

1、命名

【规范3-1-1】 变量、参数用类型缩写+大写字母开头的单词组合而成。

变量的名字应当使用
“小写缩写类型字符”(参考表) + “名词”
“小写缩写类型字符”(参考表) + “形容词/动词” + “名词”

BOOL bRes;

int nDrawMode;

常见的缩写:

//缩写(前缀)类型 

b        Boolean /bool/BOOL

n        Integer /int

str      String,char[],CString

p        Pointer //指针

h        Handle

fn       Function    

变量命名容易分两种极端情况,一种特别长,一种特别短,这里需要注意。

例如:

  1. int nPrivateDTCSaveToFile; ///< 特别长,意思够丰富,但啰嗦
  2. Cfile file; ///< 缺少必要信息,等于没有任何含义。

建议:变量命名采用2-3个单词。

 

关于缩写的约束:
【规范3-1-2】 禁止使用拼音首字母进行创造性缩写。除了行业性缩写用语可以用缩写,其他不建议用缩写。

例如:WH(维护),这样的用法是禁止的

行业性缩写,例如:GYK(轨道车运行控制)、CIR、MMI、DMI,这些有些是英文缩写,有些是中文缩写,但已经形成行业用语,可以采用大写缩写方式。

2、静态变量

【规范3-2-1】 静态变量加前缀s_(表示static)。

void Init()
{   
    static int s_nAllValue; //静态变量 
}

 

//如果静态变量又是全局或成员变量(不推荐这样操作),应该采用如下方式:

   ms_nVal

   gs_nVal

 

3、全局变量

【规范3-3-1】 如果需要定义全局变量,则使全局变量加前缀g_(表示global)

 

int g_nManyPeople; ///< 全局变量

int g_nMuchMoney;  ///< 全局变量

 

4、类成员变量

【规范3-4-1】 类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名。

void Object::SetValue(int nWidth, int nHeight)
{
   m_nWidth = nWidth;
   m_nHeight = nHeight;
}

 

5、常量

【规范3-5-1】 常量全用大写的字母,用下划线分割单词。

//C++ 写法如下:(不允许 #define 方式,不允许混合写法)

const int MAX_DATALEN = 100;

const int MIN_LENGTH = 100;

 

//C写法如下:

#define MAX_DATALEN 100

#define MIN_LENGTH 100

 

6、类名、函数名

【规范3-6-1】 类名和函数名用大写字母开头的单词组合而成。

 

class RunWorker  ///< 类名称可能是某些功能的集合也可能是某个类型,建议名词

 

int SendData()   ///< 函数一般来说表示具备什么功能,因此建议采用动名词组合

 

7、结构体命名

【规范3-7-1】 自定义结构体名称要以“_tag”为开头。

typedef struct _tagWorkerSet
{           
    byte byPipe;   
    byte byIsCAN;
    byte byClass;
};

 

8、枚举类型 命名

【规范3-8-1】 枚举类型采用全用大写的字母,用下划线分割单词。

enum OPER_RES
{
    OPER_SUCCESS = 0,           ///< 成功
    OPER_COPYING = 1,           ///< 正在复制(上次拷贝没结束)

    OPER_FREE = 3,              ///< 空闲
    OPER_DIR = 100,             ///< 操作文件夹(内部)
    OPER_FILE = 101,            ///< 操作文件(内部)

    OPER_ERROR = -1,            ///< 普通错误
    OPER_OPENDIR_ERROR = -2,    ///< 打开文件失败
    OPER_NO_DIR = -3,           ///< 无效文件夹(不存在,或路径错误)
    OPER_NO_COPYFILE = -4       ///< 不需要拷贝,文件都认为相同
};

 

 

代码行为规范

     Q:代码行为也有规范?

     随意性的代码不仅与整体风格,格格不入,也会导致代码混乱,甚至导致隐藏bug。。。。说起来很严重

    

1、总体原则

【规范4-1-1】 一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。

//下列代码是不允许
int nMode = 0, nCount;  ///< 定义了多个变量
DoFun(); RunApp();      ///< 多个执行,多件事情

【规范4-1-2】 if、for、while、do 等语句自占一行,执行语句不得紧跟其后。

if(nLevel == PLEVEL_HIGH)
{
    m_lsHigh->Add(pAdd);
}
else if(nLevel == PLEVEL_REPEAT)
{
    pAdd->nOldTick = GetTickTime();
    m_lsRep->Add(pAdd);
}
else
{
    m_lsLow->Add(pAdd);
}

 

【规范4-1-3】 if语句 必须考虑else的处理。 对于主要逻辑判断,必须写else处理,如果改else是正常不出现的,那么else处理归结为异常处理。

//默认是要打开
if(m_pSerialList[i]->OpenPort() > 0)
{
    //DEBUG_LOG02(LOG_IF_SERIAL,"open Serial port successful.",pSet->stP.byPipe,pSet->byPort);  
    m_pSerialList[i]->StartRun();
    //....
}
else
{
    //else 不允许不处理
    DEBUG_LOG02(LOG_IF_SERIAL,"open Serial port failed.",pSet->stP.byPipe,pSet->byPort);   ///< 此处else,理论上可以不处理,但要归结为 异常处理(例如日志记录)    
}

 

2、运算符与表达式

【规范4-2-1】 如果代码行中的运算符比较多(超过2个),用括号确定表达式的操作顺序,避免使用默认的优先级

【规范4-2-2】 如果是算术运算符(例如:加 减 乘 除)且超过2个,可以不用括号进行确定操作顺序。

【规范4-2-3】 如果全部是相同的运算符且超过2个,可以不用括号进行确定操作顺序。

为了防止产生歧义并提高可读性,应当用括号确定表达式的操作顺序。

nGetData = (nHigh << 8) | nLow;   ///< 用括号表示优先级

if ((bResR | bResG) && (bResR & bResB))

PS
&& 操作符(同理||操作符,也是有存在类似问题),关系是当前面条件为false时,后面的是不会被执行。因此不推荐以下做法:
if(bRes && CallFun()) ///< 此时若bRes为false,那么CallFun是不会被执行

这里也很容易出错,往往也很难理解,甚至不同编译器对优先级理解不一样。

 

3、if 语句

3.1 布尔变量与零值比较

【规范4-3-1】 不可将布尔变量直接与TRUE、FALSE 或者1、0 进行比较。

根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。
TRUE 的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE 定义为1,而Visual Basic 则将TRUE 定义为-1。

//假设布尔变量名字为bFlag,它与零值比较的标准if 语句如下:
if (bFlag)  // 表示flag 为真
if (!bFlag) // 表示flag 为假

//其它的用法都属于不良风格,例如:
if (bFlag == TRUE)
if (bFlag == 1 )
if (bFlag == FALSE)
if (bFlag == 0)

3.2 整型变量与零值比较

【规范4-3-2】 应当将整型变量用“==”或“!=”直接与0 比较。

//假设整型变量的名字为nValue,它与零值比较的标准if 语句如下:
if (0 == nValue)
if (nValue != 0)

//不可模仿布尔变量的风格而写成
if (nValue) ///< 会让人误解nValue 是布尔变量
if (!nValue)

 

3.3 浮点变量与零值比较

【规范4-3-3】 不可将浮点变量用“==”或“!=”与任何数字比较。

//千万要留意,无论是float 还是double 类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。
//假设浮点变量的名字为fData,应当将
if (0.0 == fData) ///<  隐含错误的比较

//转化为
if ((fData >= -EPSINON) && (fData <= EPSINON))

//其中EPSINON 是允许的误差(即精度)。

 

3.4 指针变量与零值比较

【规范4-3-4】 应当将指针变量用“==”或“!=”与NULL 比较。

指针变量的零值是“空”(记为NULL)。尽管NULL 的值与0 相同,但是两者意义不同。假设指针变量的名字为pPen,它与零值比较的标准if 语句如下:

if (NULL == pPen) ///< pPen 与NULL 显式比较,强调pPen 是指针变量
if (pPen != NULL)
//或(前提必须符合命名规范)
if (pPen) 
if (!pPen)

//不要写成
if (pPen == 0)   ///< 容易让人误解p 是整型变量
if (pPen != 0)

4、switch 语句

【规范4-4-1】 每个case 语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠)。

【规范4-4-2】 若某个case不需要break一定要加注释声明,便于明白是故意不加而不是忘记。

【规范4-4-3】 不要忘记最后那个default 分支。即使程序真的不需要default 处理,也应该保留语句default: break;

switch(nParam)
{
    case NOPER_UPDATE:     ///<升级状态
        DoUpdate();
        break;
    case NOPER_RUN:        ///<开始运行
        RunApps();
        break;
    default:
        break;
}

 

5、循环语句(for / while)

【规范4-5-1】 为了防止循环失去控制,尽量不在循环体内修改循环条件变量,首先是规避,然后如果无法规避,必须注释清楚。

for (int i = 0; i < NMAX_RUNSTATE; i++)
{
    if(pMain->m_lsAppRun[i] != NULL)
    {         
         //...
    }
    else
    {
         i = 0;  ///< 虽然此处理论上不存在,但如果出现,必然是死循环
    }
}

6、关于空格、空行
【规范4-6-1】 连续超过10行的代码必须要添加空行。

原则:
1、 不同性质的代码(或变量)之间要有空行
2、 相同性质的代码应该放在一起,超过10个的,也要添加空行
3、 性质是指 属性、含义、功能相似等条件,可以根据实际情况来区别
    

    //初始化
    m_byKeyUsed = 0x00;
    m_byKeyBit7 = 0x80;
    m_nCountSend = 0;
    m_byLastKey = 0x00;

    //读取数据(采用定时器读取数据,不采用事件,方便移植到linux)
    m_pRecvTimer = new QTimer(this);
    m_pRecvTimer->setInterval(50);
    connect(m_pRecvTimer,SIGNAL(timeout()),this,SLOT(ReadKeyCom()));

    //发送数据
    m_pSendTimer = new QTimer(this);
    m_pSendTimer->setInterval(50);
    connect(m_pSendTimer,SIGNAL(timeout()),this,SLOT(WriteKeyCom()));

    m_pSendTimer->start(50);
    m_pRecvTimer->start(50);

    //显示刷新
    ShowToObj(m_byNowKey);
    //打开串口
    OpenKeyCom();

【规范4-6-2】 关键字之后要留空格。
像 const、virtual、inline、case 等关键字之后至少要留一个空格,否则无法辨析关键字。
像 if、for、while 等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

【规范4-6-3】 赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,的前后应当加空格。 *
如“=”、“+=” “>=”、“<=”、“+”、“、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。

void OnReadFile(int nX, int nY, int nZ); ///< 良好的风格
void OnReadFile (int nX,int nY,int nZ);  ///< 不良的风格

if (nYear >= 2000) ///< 良好的风格
if(nYear>=2000)    ///< 不良的风格

 

函数规范

     Q:函数有哪些地方需要注意的?

     函数除了名称之外,还有参数,以及必要输入,输出等约束

     何为圈复杂度?

1、参数传递效率

【规范5-1-1】 如果输入参数以值传递的方式传递对象(类型为类、结构体),则用“const & ”方式来传递

这样可以省去临时对象的构造和析构过程,从而提高效率。对于类型为常规类型,如char int等,建议采用直接传值的方式。例如:

//C++ 写法要求:

GetErrorInfo(const _tagRunInfo &stRun)

 

2、出入口检查,安全

【规范5-2-1】 在函数体的“入口处”,对参数的有效性进行检查。

int CLocalSocket::SendData(const byte byOrder,const byte *pbyBuf,const int nLen)
{
    if((nLen > MAXLENGTH_FRAME) || (nLen < 0) || (pbyBuf == NULL))
    {
        return -1;
    }

//....
}

 

参数必须要判断:
1. 指针类型的参数

对于其他类型 如 int,建议根据实际情况进行判断。

3、入口唯一

【规范5-3-1】 任何函数只能有一个入口;即只能使用函数调用的方式进入函数,不能使用goto等语句进入函数内部。

4、圈复杂度

【规则5-4-1】 函数的圈复杂度应小于20。

圈复杂度用来衡量一个模块判定结构的复杂程度,数量上表现为独立线性路径条数,即合理的预防错误所需测试的最少路径条数。
圈复杂度大说明程序代码可能质量低且难于测试和维护,根据历史经验,程序的可能错误和高的圈复杂度有着很大关系。

圈复杂度计算方法:

  • 如果一段源码中不包含控制流语句(条件或决策点),那么这段代码的圈复杂度为1,因为这段代码中只会有一条路径;
  • 如果一段代码中仅包含一个if语句,且if语句仅有一个条件,那么这段代码的圈复杂度为2;
  • 包含两个嵌套的if语句,或是一个if语句有两个条件的代码块的圈复杂度为3,依次类推。

5、代码行数要求

【规则5-5-1】 一个函数原则上要求代码行数不超过200行(包含注释、空行)。

代码行数比较多,说明开发人员缺乏进一步思考,仅关注功能实现。
代码行数比较多,意味着逻辑复杂、思路不清晰,存在代码隐患就比较高。
代码行要求200行以内已经是非常宽泛的要求了,实际应该在50行以内为最佳。

PS
另外建议,单个 类(.cpp文件)或.c文件源代码行控制在1000行以内。否则就应该先考虑是否可以进行优化设计。

 

内存使用规范

     Q:内存使用本身就是C++的精华,也是把双刃剑。

     除了细心一点,还要注意一些原则。否则出了事情,都是大事情。

1、防止产生“野指针”

【规范6-1-1】 释放了内存之后,立即将指针设置为NULL,防止产生“野指针”,除非该指针变量本身将要消亡。

【建议】申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存;

delete m_pTxOri;
m_pTxOri = NULL;   ///< 及时变成NULL


delete[] m_pWriteBuf;
m_pWriteBuf = NULL;   ///< 及时变成NULL

野指针指向一个已删除的对象或未申请访问受限内存区域的指针。
与空指针不同,野指针无法通过简单地判断是否为 NULL避免,而只能通过养成良好的编程习惯来尽力减少。
对野指针进行操作很容易造成程序错误。

 

以下一些手段可以有效控制:

  1. new delete 在一个函数内,最好是在一个屏幕可视范围内
  2. new出来的对象,不要被其他对象进行引用
  3. 特别是结构体的成员或类成员是指针,要注意被引用的可能。

像例子的那样,一般也还好,比如这样,就麻烦:

byte *pBuf = m_pTxOriBuf;

//do somethings

delete[] m_pTxOriBuf;
m_pTxOriBuf = NULL;   ///< 及时变成NULL

//.....

//...
//do somethings
if(pBuf != NULL)
{
    //此时pBuf 是野指针了,比较容易忽视这一点。
    pBuf[0] = 0;
}

再来一个比较隐蔽的例子:

void CallFun1(byte *pBuf)
{
//....
    delete[] pBuf;
    pBuf = NULL;
}


void CallFun2(byte *pBuf)
{
//....
    pBuf[0] = 0x00;
    //....
}



void main()
{
    byte *pTxBuf = new byte[250];

    //do somethings

    CallFun1(pTxBuf);

    //.....

    //...
    //do somethings
    if(pTxBuf != NULL)
    {
        //此时pBuf 是野指针了,比较容易忽视这一点。
        CallFun2(pTxBuf);
    }
}

 

2、数组越界

【规范6-2-1】 避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

数组越界会带来不可预期的问题,也有可能运行时不报错。所以经常会犯错,特别是一些莫名其妙的错误,往往是越界导致。

//eg1:
char strName[20];
for(int i = 0;i <= sizeof(strName);i++)  ///< 已经越界
{
    strName[i] = 0x30 + i;
}



//eg2:
const int MAX_NMAE_LEN = 16; ///< 原先是10,由于协议改变,现在变成16
char strName[10];           ///< 此处未及时改过来
for(int i = 0;i < MAX_NMAE_LEN;i++)  ///< 已经越界
{
    strName[i] = 0x30 + i;
}

 

容易导致越界:
数组下标是变量,当特殊情况下,变量可能变成-1或很大一个值
协议内容定义变化,原先协议是没问题,后来协议增加了,导致问题出现

 

3、内存泄露

【规范6-3-1】 当对象消亡时确保指针成员指向的系统堆内存全部被释放,否则会造成内存泄露。

另外应当注意,如果是用第三方一些API或机制,一定要注意是否有内存泄露的风险。比如在GDI中,资源如果不回收,那么就会内存泄露。

Image* pImage = Image::FromFile(L"E:\\bac.bmp");
Graphics g(pDC->m_hDC);
g.DrawImage(pImage,0,0);


delete pImage;             ///< 必须,否则泄露。这个取决API函数的特性
g.ReleaseHDC(pDC->m_hDC);

 

【规范6-3-2】 动态内存的申请与释放必须配对(特殊应用可特殊处理,但不建议把特殊当成常规),不需要的内存应被及时释放,防止内存泄漏。
并且new、delete和new[]、delete[]要成对使用。特别要注意中间return。

不匹配也会导致内存泄露的。

 

 

 

 

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
.NET C#代码规范是一组规则和标准,用于指导开发人员在编写C#代码时如何组织、命名和编写代码,以确保代码的可读性、可维护性和一致性。 在.NET C#代码规范中,一般会包含以下方面的内容: 1. 命名规则:指定了不同类型的命名规范,包括类、方法、变量、常量等的命名规则。例如,类名使用大驼峰命名法(PascalCase),变量和方法名使用小驼峰命名法(camelCase)。 2. 代码结构和布局:指定了代码中的缩进、换行和空白字符的使用规则,以提高代码的可读性。例如,使用适当的缩进和空行来组织代码块。 3. 注释规范:指定了如何编写注释,以提供清晰的解释和文档。例如,使用XML注释格式来描述方法、参数和返回值的含义。 4. 错误处理和异常规范:指定了如何处理错误和异常情况,以确保代码的健壮性和可维护性。例如,使用try-catch块来捕获和处理异常。 5. 代码复用和模块化规范:指定了如何重用代码和模块,以提高代码的可维护性和可扩展性。例如,使用类库和模块化的设计来封装可重用的功能。 6. 性能和优化规范:指定了如何编写高性能的代码,并提供了一些优化建议。例如,避免不必要的内存分配和循环嵌套,使用合适的数据结构和算法等。 7. 安全规范:指定了如何编写安全的代码,以防止潜在的安全漏洞。例如,避免直接拼接SQL语句,而应使用参数化查询来防止SQL注入攻击。 通过遵守.NET C#代码规范,开发人员可以编写出一致、可读性强、易于维护和面向对象的代码,从而提高代码质量和开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值