今天遇到个之前觉得很常见的代码写法, 在成员方法中直接调用成员变量, 代码A 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test
{
public static Random Random { get; set; }

public void TestRandom()
{
this.Random = new Random();

if(this.Random == null)
return;

var num = this.Random.Next();

var num1 = this.Random.Next();
}
}

存在的问题

问题一, 在多线程下可能存在问题

在多线程下可能存在执行this.Random == null之后, 被其他线程将成员变量Random将其改为null, 这样将会导致空指针

因此使用局部变量接收成员变量的引用, 代码B 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test
{
public Random Random { get; set; }

public void TestRandom()
{
this.Random = new Random();

var random = this.Random;

if(random == null)
return;

var num = random.Next();

var num1 = random.Next();
}
}

问题二, 性能上访问成员变量不如访问局部变量快

直接调用成员属性生成的汇编

var num = this.Random.Next();

1
2
3
4
5
6
7
8
9
10
11
00007FF7E7A85D6D  mov         rcx,qword ptr [rbp+80h]  
00007FF7E7A85D74 call 方法存根对象: TestVariable.Test.get_Random() (07FF7E7A859B0h)
00007FF7E7A85D79 mov qword ptr [rbp+38h],rax
00007FF7E7A85D7D mov rcx,qword ptr [rbp+38h]
00007FF7E7A85D81 mov rax,qword ptr [rbp+38h]
00007FF7E7A85D85 mov rax,qword ptr [rax]
00007FF7E7A85D88 mov rax,qword ptr [rax+40h]
00007FF7E7A85D8C call qword ptr [rax+28h]
00007FF7E7A85D8F mov dword ptr [rbp+34h],eax
00007FF7E7A85D92 mov ecx,dword ptr [rbp+34h]
00007FF7E7A85D95 mov dword ptr [rbp+5Ch],ecx

调用局部变量生成的汇编

var num = random.Next();

1
2
3
4
5
6
7
8
00007FF7E7A88D27  mov         rcx,qword ptr [rbp+48h]  
00007FF7E7A88D2B mov rax,qword ptr [rbp+48h]
00007FF7E7A88D2F mov rax,qword ptr [rax]
00007FF7E7A88D32 mov rax,qword ptr [rax+40h]
00007FF7E7A88D36 call qword ptr [rax+28h]
00007FF7E7A88D39 mov dword ptr [rbp+24h],eax
00007FF7E7A88D3C mov ecx,dword ptr [rbp+24h]
00007FF7E7A88D3F mov dword ptr [rbp+44h],ecx

直接调用成员属性每次都需要去找到对应的成员的地址再调用, 而局部变量则不需要

结论

如果在一个方法内需要多次调用一个成员变量/成员属性/静态成员变量, 建议使用一个局部变量存储引用, 通过局部变量去调用。

Git开发模式分类

开发模式从本质上可以分为两类: 特性分支开发模式(Feature Branch Development)主干开发模式(Trunk Based Development).

特性分支开发模式

介绍

特性分支开发模式是指为一个或多个特定的需求 / 缺陷 / 任务创建代码分支(branch),在其上完成相应的开发(一般经过增量测试)后,把它合并(merge)到主干 / 集成分支的开发模式。

通常这种分支生命期会持续一段时间,从几天到几周不等,极少数情况甚至以月算。

以Git-Flow为例:

优点

特性开发周期宽松:因为生命期可以较长,较大的需求特性可以在宽松的时间内完成再合入主干;

分支测试的时间宽松:因为生命期可以较长,可以有较多时间对分支进行测试,甚至手工测试;

缺点

分支管理复杂:原因在于大量采用代码分支,且来源分支和合入目标分支各异,操作复杂 —— 以上图为例,可以从 master(Tag 1.0.0) 拉出 hotfix 1.0.2 分支,然后合入到 develop 分支,开发阶段结束后合入到 release branches,发布后合入 master,非常复杂,很容易出错;

合并冲突多、解决难:分支生命期越长,意味着与主干的代码差异越大,冲突概率越高,冲突的解决难度越大(甚至成为不可能);

迭代速度慢:特性分支生命期长(数天至数周)意味着特性上线速度慢,相应的迭代速度也慢;

需要较多测试环境:每个特性分支都需要分配至少 1 个测试环境,且长期占用(有状态);

适用环境

对版本迭代速度要求不高

测试自动化程度低,或说主要靠人工测试的

常用模式

Git-Flow, Github-Flow, Gitlab-Flow

主干开发模式

介绍

主干开发,是指开发人员直接向主干(习惯上主干分支通常为:trunk 或 master)提交 / 推送代码。通常,开发团队的成员 1 天至少 1 次地将代码提交到主干分支。在到达发布条件时,从主干拉出发布分支(通常为 release),用于发布。若发现缺陷,直接在主干上修复,并根据需要 cherry pick 到对应版本的发布分支。

以TBD Flow为例:

优点

分支模型简单高效,开发人员易于掌握不容易出现错误操作

避免了分支合并、冲突解决的困扰

随时拥有可发布的版本

有利于持续集成和持续交付

缺点

基础架构要求高:合入到主干的代码若质量不过关将直接阻塞整个团队的开发工作,因此需要高效的持续集成平台进行把关;

自动化测试要求高:需有完备单元测试代码,确保在代码合入主干前能在获得快速和可靠的质量反馈;

最好有代码评审:若代码质量要求高,需要配套代码评审(CR)机制,在代码提交到主干时,触发 CR,通过 Peer Review 后才能正式合入;

最好有特性开关:主干开发频发合入主干的情况下,特性拆分得很小,可能是半成品特性,需要配套特性开关(Feature Toggle),只有当特性整体开发完才通过灰度发布等手段逐步打开;

适用环境

对迭代速度要求高,希望需求快速交付上线

基础架构强,持续集成工具高效;

团队成员习惯 TDD(测试驱动开发),代码自动化测试覆盖率高(至少增量代码的自动化测试覆盖率高);

常用模式

TBD Flow, TBD++ Flow

在学react的代码的入门例子的时候遇到了以下代码

1
const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');

当时想当然的认为并不需要括号,如以下代码

1
const status = 'Next player: ' + this.state.xIsNext ? 'X' : 'O';

但显示结果会为X

但是该代码实际上在类型严格的语言如C#会报错, 因为代码在没有加括号会从左到右来执行, 这样会将'Next player: ' + this.state.xIsNext先计算, 因此得到一个string类型, 但强类型语言的string没法转换成bool类型, 因此会编译不通过.

但是js是个弱类型的语言, 因此string类型也能进行true和false判断, string不为null则为true, 反之为false, 因此'Next player: ' + this.state.xIsNext先计算, 进行真假判断, 得到结果为true, 所以界面会显示X

将多个对象的所有枚举属性拷贝到目标对象上

1
2
3
4
5
6
7
8
var target={a:1,b:2};
var source1={b:3,c:4};
var source2={c:5,d:6};

var newTarget=Object.assign(target,source1,source2);

console.log('target '+target);
console.log('newTarget '+newTarget);

输出:

1
2
target {a:1,b:3,c:5,d:6}
newTarget {a:1,b:3,c:5,d:6}

拷贝出新对象, 不修改原对象

1
2
3
4
5
var target={a:1,b:2};
var newTarget=Object.assign({},target,{c:3});

console.log('target '+ target);
console.log('newTarget '+ newTarget);

输出:

1
2
3
target {a:1,b:2}

newTarget {a:1,b:2,c:3}

github创建Pages的网页静态页面仓库

github仓库名为 github用户名.github.io

github创建blog的仓库

名字随便

hexo 安装

安装nodejs

安装git

情况npm缓存

1
npm cache clean --force

修改npm镜像源为npmmirror(淘宝镜像源已经过期)

1
npm config set registry https://registry.npmmirror.com

安装hexo

1
npm install -g hexo-cli

hexo 初始化

1
2
3
4
hexo init blog
cd blog
npm install
hexo server

安装hexo通过git发布的插件

1
npm install hexo-deployer-git --save

修改_config.yml文件

1
2
3
4
deploy:
type: git
repository: https://github.com/github用户名/github用户名.github.io.git
branch: main

安装 NexT 模板

1
2
npm install hexo-theme-next
git clone https://github.com/next-theme/hexo-theme-next themes/next

修改_config.yml文件 theme: next

hexo 配置

修改_config.yml文件

同步生成文件夹 post_asset_folder: true

修正路径(渲染器设置)

1
2
3
marked:
prependRoot: true
postAsset: false

安装pandoc

  1. 下载并安装pandoc:下载地址

  2. 选择安装到全部用户

  3. 检查_config.yml中是否有以下配置

    1
    2
    pandoc:
    pandoc_path: C:/Program Files/Pandoc/pandoc.exe

更换渲染器

1
2
npm un hexo-renderer-marked --save
npm i hexo-renderer-pandoc --save

配置数学公式

1
2
npm install hexo-filter-mathjax
hexo clean

如果需要使用 MathJax 来加载公式,对应文档的的文件头加入一个选项:mathjax: true,可以根据个人需求是否加入位于 scaffolds/post.md 文件模板中。

Hexo命令

启动hexo服务

1
hexo s

本机预览草稿

1
hexo S --draft

生成静态文件

1
hexo g

发布静态文件

1
hexo d

创建文章

1
hexo new "My New Post"

创建草稿

1
hexo new draft "new draft"

查看草稿

1
hexo server --drafts

发布草稿为文章

1
hexo publish [layout] <filename>
1
hexo publish draft <filename>

参考资料

https://hexo.io/docs/

http://home.ustc.edu.cn/~liujunyan/blog/hexo-next-theme-config/

RSA是一种"非对称加密算法"

相关知识

互质关系

如果两个正整数除1以外没有其他公因子,我们就称这两个数互质(coprime)。

比如:15和32,没有公因子,所以它们是互质关系。这说明不是质数也可以构成互质关系。

a*b=c, a和b是c的因子

补充:

质数,指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

公因数(公约数),它是一个能被若干个整数同时均整除的整数。如果一个整数同时是几个整数的约数,称这个整数为它们的“公约数”;公约数中最大的称为最大公约数。对任意的若干个正整数,1总是它们的公因数。

公因子,是一个数学概念,指的是能同时整除几个整数的整数,可以用辗转相除法算出。

加密的原理

公钥(E, N), E:指数, N:模数

公式:

假设公钥为(7, 33), 私钥为(3, 33), 私钥的指数是不公开的, 公私钥的模数会是相同的

假设数据源为:

C, A, O

将C, A, O转换成十进制为:

3, 1, 15

使用公钥的指数进行求幂:

, ,

2187 ,1 , 170859375

使用公,私钥的模数进行求余:

, ,

得到密文

9, 1, 27

解密的原理

私钥(D, N), D:指数, N:模数

公式:

收到的密文

9, 1, 27

使用私钥的指数进行求幂:

, ,

729, 1, 19683

使用私钥的模数求余:

, ,

得到原文:

3, 1, 15

公钥和私钥生成的原理

  1. 选出两个质数
  2. 质数相乘
  3. 欧拉函数
  4. 选出公钥E 质数;1<公钥<T;不是T的因子
  5. 算私钥D
0%