iOS代码开发详细规范

一、命名

1.属性对象和局部变量(采用小驼峰命名法)

UI视图对象

变量名称 + (Label / Button / Cell / TableView / WebView / ScrollView / CollectionView / ImageView / TabBar / TextField ...)
如:UILabel *titleLabel;
特殊类型:UIBarButtonItem -> ButtonItem.

非UI视图对象

VC: name + (VC / TableVC / CollectionVC / PageVC)
如: UIPageViewController *firstPageVC;
特殊类型:

  • UINavigationController -> NavCtroller
  • UITapGestureRecognizer -> TapGesture
  • UILongPressGestureRecognizer -> LongPressGesture

数据类型: name + 类型(Integer / Int / Long / Float / Double / Number / String / Array / MutableArray / IndexPath / Date / Error...)
如:NSArray *dataArray;
特殊类型:

  • NSDictionnary -> Dict
  • NSMutableDictionnary等可变类型变量,使用不可变类型的命名方式如:name + Dict
  • NSTimeInterval -> Double
  • CGRect -> Rect, CGSize -> Size

注意:1.局部变量可不遵守变量名字后面加数据类型这一规则。 2.Bool类型以is、has、can等作为前缀,或以ing、ed等作后缀。

其他:

  • name + Model
  • name + ViewModel
  • name + Request(网络请求)
  • name + Block

注意:尽量为每个变量取有意义的名字,即“name”,但若确实没有name的情况下,直接采用如下形式,如:UITableView *tableView;

2.实例变量

实例变量采用小驼峰命名法的基础上,以下划线“_”作为前缀,如:
UIButton *_loginButton;

3.常量 和 宏常量

普通常量:以小写字母“k”开头的驼峰命名法,如:static NSString *const kMovieCellHeight;
通知常量:一般形式为[ 触发通知的类名] + [Did 或 Will] + [ 动作 ] + Notification ;
宏常量:全部大写,中间用下划线“_”作间隔,如:#define TARGET_OS_IOS
注意:

  • 如果宏常量和APP业务相关则添加相应业务类名作前缀,如果适用于整个APP则直接使用APP前缀,如:#define MOHomeCellHeaderHeight (18.0)
  • 尽量不用宏来定义常量,而是采用枚举和const定义的常量

4.枚举类型名称 和 枚举变量名称

定义枚举类型:采用OC风格定义枚举类型,举个栗子:

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {  
    UIViewAnimationTransitionNone,         //默认从0开始  
    UIViewAnimationTransitionFlipFromLeft,  
    UIViewAnimationTransitionFlipFromRight,  
    UIViewAnimationTransitionCurlUp,  
    UIViewAnimationTransitionCurlDown,  
};

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {  
    UIViewAutoresizingNone                 = 0,  
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,  
    UIViewAutoresizingFlexibleWidth        = 1 << 1,  
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,  
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,  
    UIViewAutoresizingFlexibleHeight       = 1 << 4,  
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5  
};

其中:    
1.枚举类型名称采用首字母大写的驼峰命名法,并且添加相关类作为前缀    
(本例中相关的类为UIView)
2.枚举值命名要添加枚举类型名作前缀    
(本例中枚举值前缀分别为UIViewAnimationTransition和UIViewAutoresizing)

枚举变量名称和类型名称基本保持一致的情况下,尽量增强其可阅读性,采用小驼峰命名法,如:

UIViewAnimationTransition animationTransitionType; 

5.方法名

采用小驼峰命名法,做到见其名知其含义,本身具有充分的解释性,拒绝无意义命名。
例外情况:

  • 可以用一些通用的大写字母缩写打头的方法,比如 PDF,TIFF
  • 可以用带下划线的前缀来命名私有方法或者类别中的方法

6.类名 和 协议名

类名:“统一的类名前缀” + “功能模块简称”(可省略) + “描述该类的词或词组” + 控件类型名尾缀(如Cell,ViewController,View等,可省略)
协议名:类名 + “Delegate”
都采用大驼峰命名法。

7.分类的名称 和 分类中方法名

分类的名称和类名的命名方式基本一致,只是前缀由开发者指定。
但分类中的方法名,需使用分类的前缀小写形式 + “_”的形式,如:

@interface NSDate (MOLocalDate)

/// 获取根据时间间隔按照系统时间计算的日期,seconds:单位(秒)
+ (NSDate *)mo_dateWithTimeIntervalSince1970:(NSTimeInterval)seconds;    


8.Assets.xcassets文件命名

1.功能模块使用的资源,使用功能模块代码资源同样的文件夹结构(一级或多级)和名称。
2.公共或通用资源文件夹取名为“Common”或“Normal”
3.图片资源命名方式采用全部小写和分割线分隔的形式,前缀为功能模块名称或缩写,如:
common_navigation_bar_back

9.功能模块内文件夹命名

一般情况下,各功能模块下的文件夹命名包括但不限于以下命名:

  • View 或 CustomView
  • Model
  • ViewController
  • Cell
  • ViewModel (MVVM模式下使用)
  • Helper
  • ……

10.Xib或Storyboard中命名

  • Xib文件名和对应类名保持一致
  • Xib或者Storyboard中的控件,命名采用全部大写且用一个空格分隔的形式。添加控件时立即添加有意义的命名,方便查看不同控件的约束关系以及后期维护。

总结:命名尽量不要用单词简写,注重保持语义明确;名称保持一致性,避免同样语义出现多种单词如“number”和“count”;命名风格统一,遵守统一的的命名规则。

二、风格

1.关于空格与空行

  • 数组或字典字面量用逗号隔开的形式书写时,逗号后留有一个空格
  • ifswitch语句中,这两个关键字和紧跟的括号之间留有一个空格
  • 二元符号如"="、"=="、">"、"<"、"||"、"&&"等两侧留有一个空格
  • 三元符号中"?"和":"两侧留有一个空格
  • 方法名“+”和“-”号和方法名中间留有一个空格;方法实现中“{”和方法名之间留有一个空格。
  • 定义属性时,逗号后留有一个空格,如:
@property (nonatomic, copy, readonly) NSString *titleString;    
小括号左右两边各用一个空格与外部隔开;
星号“*”左边留有一个空格。
  • 声明类时
@interface MOCalendarService : NSObject
冒号左右各用一个空格隔开
  • 声明分类或延展(类目)时
/// 分类
@interface NSDate (MOLocalDate)
/// 延展(类目)
@interface ClassName () <UICollectionViewDelegate,    
 UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

- 小括号左右两边各用一个空格与外部隔开
- 协议列表中,逗号后留有一个空格
  • 方法与方法之间空一行,方法内的代码块与代码块之间都空一行
  • #prgma mark - Demo分隔语句上方留有一空行,下方不留空行或留有一空行。

2.注释

  • 在需要注释的方法或属性上方使用Command + Alt + /”快捷键,或者“/// + 一个空格 + 说明”的形式进行注释,方便别处使用“Alt + 鼠标点按”直接查询该方法或属性的定义。
    (注意:在属性右侧使用“/// + 一个空格 + 说明”的形式进行注释,在别处无法用“Alt + 鼠标点按”快捷键获取注释详情)
  • 针对方法内某一行代码语句进行注释,可以在此行代码上方或右侧进行“// + 一个空格 + 说明”的形式进行注释,注意将该代码段和其它代码段用注释行或空白行进行分隔。
  • 针对方法内某几行代码段进行注释,也注意将该代码段和其它代码段用注释行或空白行进行分隔。

总结:

  • 注释方法和属性尽量方便代码使用时用快捷键获取注释内容
  • 注释内容尽量让不懂代码的人能看懂在做的事情
  • 注释代码行或代码段要明确区分注释区域
  • 典型的需要注释的地方包括:头文件中类名、属性和方法注释,ifswitch语句注释,魔术数字或字符串注释
  • 注释不要和Mark混淆使用。

3.Mark分区

建议开发时使用Xcode右下角的代码快捷(Code Snippet Library)方式保存和提取常用文件Mark分区结构。
针对VC:

#pragma mark - View Lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    [self buildingUI];
    [self makeViewConstraints];
    [self bindViewModel];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
}

#pragma mark - buildingUI
- (void)buildingUI {
}

#pragma mark - Make Constraints
- (void)makeViewConstraints {
}

#pragma mark - bindViewModel
- (void)bindViewModel {
}

#pragma mark - Delegate Methods

#pragma mark - Publich Methods

#pragma mark - Private Methods

#pragma mark - Notification Event

#pragma mark - Property Set

#pragma mark - Property Get

针对ViewModel:

#pragma mark - Life Cycle
- (instancetype)init {
    self = [super init];
    if (self) {
        [self binding];
    }
    
    return self;
}

#pragma mark - Bind
- (void)binding {
    
}

#pragma mark - Publich Methods

#pragma mark - Private Methods

#pragma mark - Property Set

#pragma mark - Property Get


针对TableViewController:

#import "<#ControllerName#>.h"

// static NSString *const <#cellNameId#> = @"<#cellId#>";

@interface <#ControllerName#> () <UITableViewDelegate, UITableViewDataSource>

// @property (nonatomic, strong) <#ViewModolClass#> *viewModel;
@property (nonatomic, strong) UITableView *tableView;

@end

@implementation <#ControllerName#>

#pragma mark - View Lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    [self buildingUI];
    [self makeViewConstraints];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
}

#pragma mark - buildingUI
- (void)buildingUI {
    self.title = @"<#TitleName#>";
}

#pragma mark - Make Constraints
- (void)makeViewConstraints {
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
    }];
}

#pragma mark - UITableViewDataSource && UITableViewDelegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return <#countInteger#>;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 44.f;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 0.1f;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 0.1f;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<#cellNameId#>];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:<#cellNameId#>];
    }
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
    // do something
    
}

#pragma mark - Publich Methods

#pragma mark - Private Methods

#pragma mark - Notification Event

#pragma mark - Property Set

#pragma mark - Property Get
// - (<#ViewModolClass#> *)viewModel {
//  if (!_viewModel) {
//      _viewModel = [[<#ViewModolClass#> alloc] init];
//  }
//  return _viewModel;
//}

- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        // _tableView.tableFooterView = [[UIView alloc] init];
        // _tableView.showsVerticalScrollIndicator = NO;
        // _tableView.contentInset = UIEdgeInsetsMake(0, 0, 50, 0);
        [self.view addSubview:_tableView];
    }
    return _tableView;
}

@end


针对Cell:

#pragma mark - Init Method
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self makeViewConstraints];
    }
    return self;
}

#pragma mark - 约束布局
- (void) makeViewConstraints {
}

#pragma mark - Public Methods

#pragma mark - Private Methods

#pragma mark - Property Set

#pragma mark - Property Get

针对自定义View:

#pragma mark - Init Method
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self makeViewConstraints];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self makeViewConstraints];
    }
    return self;
}

#pragma mark - 约束布局
- (void) makeViewConstraints {
}

#pragma mark - Publich Methods

#pragma mark - Private Methods

#pragma mark - Property Set

#pragma mark - Property Get

4.对齐

  • 所有代码段使用四个空格进行缩进
  • 名字太长的方法定义或调用,可采用冒号对齐的方式多行展示
  • 数组和字典字面量跨行书写时“@”符号对齐:
/// 我是数组
NSArray *demoArray = @[@"Object-C",
                       @"Swift",
                       @"Python",
                       @(YES),
                       @(1)
                      ];
/// 我是字典
NSDictionary *demoDict = @{@"Object-C":@"Object-C",
                           @"Swift":@"Swift",
                           @"Python":@"Python",
                           @"BoolValue":@(YES),
                           @"Number":@(1)
                          };
其实,上面两个例子中,中括号和大括号的始末位置也进行了对齐。
  • if语句中,左大括号“{”不跨行,并和右括号“)”之间留有一个空格; 右大括号“}”和对应的“if”保持左端对齐; else语句和左侧“}”以及右侧“{”在同一行,举个例子:
/// 我是 if 语句
if (18 == self.age) {
    if (self.isGirl) {
        NSLog(@"Marry me!");
    } else {
        /// do something
    }
}

建议使用if语句时,直接选择苹果提供的快捷代码段。

  • 所有枚举值与最左侧保持四个空格的缩进
  • 非线程安全的属性以"@property (nonatomic, ...)"打头的形式书写,代码更工整
  • block中嵌套block时,注意每个"}"和对应的代码起点对齐

三、开发习惯

1.Switch语句每个case后添加“{}”的习惯

- (void)sampleForSwitch {
    SampleEnum testEnum = SampleEnumTwo;
    switch(testEnum) {
        caseSampleEnumUndefined: {
            // do something
            break;
        }
        caseSampleEnumOne: {
            // do something
            break;
        }
        caseSampleEnumTwo: {
            // do something
            break;
        }
        default: {
            NSLog(@"WARNING: there is an enum type not handled properly!");
            break;
        }
    }
}

2.先建立实体文件夹,后在工程中引入

3.条件语句

简单条件判断推荐使用三目运算符“? :”,如:

result = object ? : [self createObject];

注意这里“?”后写成空格,会直接返回object的情况,不建议用下面这种形式:
result = object ? object : [self createObject];    


4.Bool赋值

简单的条件判断后赋布尔值的逻辑,可以省略if语句,如:

BOOL isAdult = age > 18;

而不是:
BOOL isAdult;
if (age > 18) {
    isAdult = YES;
}
else {
    isAdult = NO;
}

5.拒绝魔术数字和字符串

举个例子:

/// 反例1 “Nissan”字符串突然横空出现
if (carName == "Nissan")
/// 反例2 “18”是个什么意思~
if (age > 18) { ... }

推荐下面的方式:
/// 用枚举类型代替字符串类型(由于是数字类型比较,编译速度比字符串类型更为高效)
if (car == Car.Nissan)
/// 提前为魔术数字定义常量,同时方便添加注释(即明确数字含义后再继续写代码)
const int adultAge = 18; 
if (age > adultAge) { ... }

6.readonly属性

不需要修改属性的地方,心怀添加readonly的念想。

7.copy属性

对于有可变类型子类的数据类型,使用copy属性防止数据被更改,如NSString、NSArray、NSDictionary。

8.使用字面量方式初始化数据,增强可读性

9.复杂判断简单化

if (job.JobState == JobState.New
    || job.JobState == JobState.Submitted
    || job.JobState == JobState.Expired
    || (job.JobTitle && job.JobTitle.length) {
    ....
}

可以被优化为:
/// 首先将上面整体提炼为一个方法
if ([self canDeleteJob:job]) { ... }        
/// 方法内对条件判断进行拆分
- (BOOL)canDeleteJob:(Job *)job {
    BOOL invalidJobState = job.JobState == JobState.New
                          || job.JobState == JobState.Submitted
                          || job.JobState == JobState.Expired;
    BOOL invalidJob = job.JobTitle && job.JobTitle.length;

    return invalidJobState || invalidJob;
}

10.嵌套判断平行化

BOOL isValid = NO;
if (user.UserName) {
    if (user.Password) {
        if (user.Email) {
            isValid = YES;
        }
    }
}
return isValid;

可以被优化为:

if (!user.UserName) return NO;
if (!user.Password) return NO;
if (!user.Email) return NO;

return YES;

11.回调方法加调用者的习惯

如经典的:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

添加调用者tableView作为参数,方便信息传递和区分调用者。

12.NSTimer、观察者、通知、网络请求等注意在Dealloc或viewWillDisappear:方法中移除。同一个文件中,移除的顺序应和创建的顺序保持一致,方便后期维护(注意:工程中使用RAC或已经封装好的方法不需要作移除操作)。

13.访问或操作NSArray、NSDictionary等对象时,注意判断对象和对象内要访问的元素是否为nil。

14.虽然很简单,但尽量不用new方法而是统一采用Cocoa规范中的[[ClassName alloc] init]方法

15.Delegate使用weak属性修饰

@property (nonatomic, weak) delegate;

16.提交代码前保证无warning和error

17.随时注释别人或未来的自己有可能看不懂的代码

18.逻辑捋清楚前不要写代码,因为一定会重写。

四、设计思想

1.精简

  • 方法不超过约一百行,否则就要考虑拆分
  • 清除:无用类、方法、资源、注释、多余空行、Log语句、警告

2.分工明确

  • .h文件:核心属性和方法声明地带 (.h文件由于不参与实现过程,用@class引用类,将#import "Demo.h"形式的引入统一写在.m文件中)
  • .m文件:私有方法、私有变量声明以及所有方法的实现基地
  • 分类:不常用、或与主业务无关的方法聚居地(不要滥用分类)
  • View层:UI搭建(MVC设计模式下可以进行简单的数据展示)
  • Model层:展示最直白的数据结构
  • ViewController层:
    • 1.容器:容纳子VC,构建View布局和展示
    • 2.控制Viewmodel
    • 3.响应UI事件(或信号)和代理方法
    • 4.不同生命周期逻辑处理
  • ViewModel层:用来且只用来处理和数据相关的一切
    • 1.数据初始化
    • 2.请求网络数据
    • 3.数据业务处理:数据持久化、筛选、排序、验证、增删改查
    • 4.将处理后的数据在View上展示
    • 5.头文件中返回最终有效readonly数据
  • 网络层:分担ViewModel层网络请求职责

3.团队一致性

上述所有,共同自律,提高整体效率。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,656评论 18 139
  • iOS编程规范0规范 0.1前言 为􏰀高产品代码质量,指导广大软件开发人员编写出简洁、可维护、可靠、可 测试、高效...
    iOS行者阅读 4,459评论 21 35
  • 推荐文章:禅与 Objective-C 编程艺 前言 为􏰀高产品代码质量,指导广大软件开发人员编写出简洁、可维护、...
    WolfTin阅读 2,757评论 0 1
  • 搜狐视频iOS团队 Objective-C 编码规范 介绍 团队中长期以来存在各人不同的编码方式和习惯,导致代码中...
    DiligentLeo阅读 768评论 0 5
  • .Net 开发规范一、C# 编码规范1. 代码组织与风格1.1. Tab要使一个Tab为4个空格长。1.2. 缩进...
    PowerYangSoft阅读 3,962评论 0 3