长期从事Delphi
开发,虽不敢说精通,但说很熟悉仍是至关有自信的。不过,只会一门语言,并且仍是这么老的语言,更是在大天朝很小众的语言,总感受本身离饿死街头没多远了,因此趁着还没老再学个潮点的吧。c++
先前考虑过Python
,初步了解后以为不太适合本身:编程
解释型语言:部署时得先搞个运行环境,发布的程序就是源码自己,再加上这个执行效率,怎么想都仍是编译型语言更合适。数组
动态语言:无需声明,拿来就用,这已经很不合习惯了。想一想一个变量,前一秒仍是浮点数,下一秒就成字符串了,再一眨眼又成某个对象了……虽然通常不会有人这么写,可是挡不住手误啊,仍是把这种小细节交给编译器更让人放心。并发
因此,对于有点强迫症和洁癖的本身,最后仍是选了Go
,比较符合已有的编程习惯,学习成本应该相对会低些吧。ide
至于Go
嘛,想学是已经好久了,但因为种种缘由却迟迟未开启,不过终究仍是要迈出这一步的,因此就搞这么个系列来记录吧,一方面算是自我督促,另外一方面也算是一种交流吧,固然,若一不留神帮上了谁,那自是开心极了。函数
已经初步了解过了Go
,说来和Delphi
仍是有很多类似之处呢,从Delphi
转向Go
应该会比较轻松吧。学习
Delphi
的工程算是比较自由的,源码的话,只要把单元路径引了或是直接包含进工程单元里就能够了,编译出的dcu
和最终的exe
指定下路径也就没问题了,一般我都使用下面这种结构:测试
Project/ bin/ src/ dcu/ mod1/ *.dfm *.pas mod2/ *.dfm *.pas *.dpr
不过,每个工程都要设置,并且我习惯将Debug
和Release
设置彻底同样,也还真是够烦的。ui
Go
就没得选了,只有一种结构:编码
Project/ bin/ pkg/ src/ *.go mod1/ *.go *_test.go mod2/ *.go *_test.go
总体和我原有的习惯差很少,仍是蛮容易接受的,不过却是要把这Project
的路径加入到GOPATH
系统变量里让人有一点小不爽。可是Go
能够直接把测试都写了,这点仍是蛮让我惊喜的,毕竟用了这么多年Delphi
也没写过一行像样的测试。
Delphi
典型的源码结构是这样:
unit Unit1; interface uses ...//单元引用 type ...//公开类型定义 const ...//公开常量 var ...//公开变量 procedure Proc1;//公开过程声明 function Func1: Integer;//公开函数声明 implementation uses ...//私有单元引用 const ...//私有单元级常量 var ...//私有单元级变量 procedure Proc1; var ...//模块级变量 begin ...//公开过程实现 end; function Func1: Integer; const ...//模块级常量 begin ...//公开函数实现 end; procedure Proc2;//单元级私有过程实现 function Func2: Integer;//模块级私有函数实现 begin ... end; begin ... end; initialization ...//单元初始化代码 finalization ...//单元反初始化代码 end.
Go
的源码结构是这样:
package pkg1 import "fmt" //导入包 const C1 //公开常量 const c2 //私有常量 var V1 //公开变量 var v2 //私有变量 func Func1(){}//公开方法 func func2(){}//私有方法
总体上来看,Delphi
语意更明确,Go
则更简洁,Delphi
更集中,Go
则和全部类c语言
同样比较单1、灵活,各有各的好。却是Go
这用首字母大小写来区分公开私有让我这喜欢pascal命名法
的有点尴尬。
Delphi
//这是单行注释 { 这是多行注释第一行 这是多行注释第二行 } (* 这是另一种多行注释 与{}能够互相包含 主要用于注释内容中有 } 的状况 *)
Go
//这是单行注释 /* 这是多行注释第一行 这是多行注释第二行 */
Delphi
安装CnPack
以后,可使用xml风格
的注释,写在方法前能够成为方法的文档说明。
Go
提供的godoc
也能够从代码中提取顶级声明的首行注释以及每一个对象的相关注释生成相关文档。
Delphi
:=
+
、-
、*
、/
、div
、mod
not
、and
、or
、xor
not
、and
、or
、xor
、shl
、shr
=
、<>
、>
、<
、>=
、<=
^
、+
、-
、=
、<>
@
+
+
、-
、*
、<=
、>=
、=
、=
、<>
、in
as
、is
、=
、<>
Go
=
、:=
、+=
、-=
、*=
、/=
、%=
、<<=
、>>=
、&=
、|=
、^=
+
、-
、*
、/
、%
、++
、--
!
、&&
、||
<<
、>>
、&
、|
、^
==
、!=
、>
、<
、>=
、<=
*
&
+
Delphi
//整型 ShortInt 1B -128~127 Byte 1B 0~255 SmallInt 2B -32768~32767 Word 2B 0~65535 LongInt 4B -2147483648~2147483647 LongWord 4B 0~4294967295 Int64 8B -9223372036854775808~9223372036854775807 UInt64 8B 0~18446744073709551615 Integer 4B -2147483648~2147483647 Cardinal 4B 0~4294967295 //浮点型 Single 4B Real48 6B //从未用过 Double 8B Real 8B Currency 8B //金融专用 //布尔型 Boolean 1B ByteBool 1B WordBool 2B LongBool 4B //字符型 AnsiChar 1B //ANSI编码 WideChar 2B //Unicode编码 Char //早期版本至关于AnsiChar,后期WideChar //字符串 ShortString //兼容老旧版本 AnsiString //ANSI编码 WideString //Unicode编码 String //早期版本至关于AnsiString,后期WideString //指针 Pointer //变体类型 Variant
Go
//整型 int8 1B -128~127 uint8 1B 0~255 int16 2B -32768~32767 uint16 2B 0~65535 int32 4B -2147483648~2147483647 uint32 4B 0~4294967295 int64 8B -9223372036854775808~9223372036854775807 uint64 8B 0~18446744073709551615 int //有符号,与平台相关 uint //无符号,与平台相关 //浮点型 float32 4B float64 8B //布尔型 bool //字符型 byte //uint8的别名,一般用于代表原始数据 rune //等价于int32,一般用于表示一个UTF-8字符 //字符串 string //UTF-8编码 //指针 uintptr //复数 complex64 complex128 //map类型 map[键类型] 值类型 //接口类型 interface{} //错误类型 error
总体上看,二者数据类型基本一致,不过Go
直接支持了map和复数类型。另外,Go
也没有变体类型,不过能够用接口类型interface{}
来实现传任意类型。须要注意的是,Go
的字符串是UTF-8
编码而不是Unicode
编码。
Delphi
在数据类型兼容时可直接进行隐式转换,也可进行显式(强制)转换,语法为:类型B的值 := 类型B(类型A的值)
;Go
不存在隐式转换,须要转换时只能使用显式(强制)转换,语法为:类型B的值 = 类型B(类型A的值)
。
Delphi
//声明变量 var v1 : string; v2, v3 : Integer; //声明并初始化 var v4 : Double = 3.14; //变量赋值 v1 := 'abc';
Go
//声明变量 var ( v1 string v2, v3 int ) //声明并初始化 var v4 float64 = 3.14 var v5 = 1234 //在函数内还能够这样 v6 := 2468 //变量赋值 v1 = 'abc' v2, v3 = v3, v2
变量声明都用了关键字var
,且都是变量在前,类型在后,也都支持批量声明,声明并初始化的语法也基本一致,赋值也几乎一致。
不过Go
还多了一些特性:
支持类型推导,声明并初始化时可省略类型。
在函数内部声明并初始化变量时还支持用:=
的方式简写(不过这和Delphi
的赋值符号同样,意义却截然不同,使用时得留意了)。
函数内部的变量能够像c++
同样达到语块级的做用域和生命期。
支持多重赋值,交换变量更简洁。
支持匿名变量,即用_
来占位。
Delphi
const c1: string = 'abc'; c2 = '123'; c3 = 456;
Go
const ( c1 string = "abc" c2 = "123" c3 = 456 c4 //与c3同样 c5, c6 = 1.2, true )
都用了关键字const
,都支持批量声明,也都支持类型推导,并且Go
的类型推导更智能些,换句话说就叫更复杂些(主要是由于与别的混在一块儿了)。
Delphi
type myEnum=( eA, //0 eB, //1 eC = 10, //10 eD, //11 eE, //12 eF //13 );
Go
const( eA = iota //0 eB //1 eC = 10 //10 eD //10 eE = iota //4 eF //5 )
Delphi
使用关键字type
来声明一个枚举类型,并且还须要命名,默认以0
开始递增,也能够为某一元素指定值,其后元素依次递增。
Go
没有枚举的关键字,但使用const
和iota
也能够达到目的。iota
每次遇到const
就重置为0
,每增长一个元素就自增1
,不管元素有没有取iota
的值。
不过,通常状况下不多会为枚举的元素指定特定值。
Delphi
//定义类型 type myInt = type Integer; //类型别名 type myInt = Integer;
Go
//定义类型 type myInt int //类型别名 type myInt = int
Delphi
//静态数组 var r1 : array[0..9] of Byte; var r2 : array[0..1] of Boolean = (True, False); var r3 : array[0..3,0..1] of Integer; //二维静态数组 var r4 : array[0..3] of array[0..1] of Integer; //同上 //动态数组 var r5 : array of Integer; //一维动态数组 var r6 : array of array of Integer; //二维动态数组
Go
//静态数组 var r1 [10]byte var r2 [2]bool{true,false} var r3, r4 [4][2]int //用切片来代替动态数组 var r5 []int var r6 [][]int
Go
没有专门的动态数组,不过有更为灵活、实用的切片,用切片彻底能够实现动态数组的功能。
Delphi
//定义 type TEmp = record Name : string; Age : Integer; end; PEmp = ^TEmp; var emp : TEmp; p : PEmp; //赋值 p := @emp; emp.Name := 'Sam'; //p^.Name := 'Sam'; emp.Age := 10; //p^.Age := 10; //还有一种带packed的结构体,因为不会进行内存对齐而速度略慢,但用于dll时可避免内存混乱 type TRcd = packed record B : Byte; C : Integer; end;
Go
type TEmp struct { Name string Age int } var( emp TEmp p *TEmp ) p = *emp emp.Name = "Sam" //(*p).Name = "Sam" //p.Name = "Sam" emp.Age = 10 //(*p).Age = 10 //p.Age = 10
Delphi
//if if 布尔表达式 then ...; //if ... else ... if 布尔表达式 then ... else ...; //if ... else if ... if 条件表达式1 then ... else if 布尔表达式2 then ... else ...; //case语句 case 选项表达式 of 值1: ...; 值2: ...; else ...; end;
Go
//if if 布尔表达式 { ... } //if ... else ... if 布尔表达式 { ... } else { ... } //if ... else if ... if 布尔表达式1 { ... } else if 布尔表达式2 { ... } else { ... } //switch switch 选项表达式 { case 值1: ... fallthrough //兼容C语言的case,强制执行下一个case case 值2: ... default: ... } switch{ case 选项表达式 == 值1: ... case 选项表达式 == 值2: ... default: ... } switch 类型表达式 { case 类型1: ... case 类型2: ... default: ... }
Delphi
//升幂记数循环 for 记数变量 := 起始值 to 终点值 do //起始值<终点值,变量每循环变化1 语句; //降幂记数循环 for 记数变量 := 起始值 downto 终点值 do //起始值>终点值,变量每循环变化1 语句; //高版本支持,2009+? for 循环变量 in 集合 do 语句; //当型循环,当表达式值为真时执行循环 while 布尔表达式 do 语句; //直到型循环,当表达式值为真时退出循环 repeat 语句; until 布尔表达式;
Go
//类C的for循环 for 循环变量初始化; 条件表达式; 循环变量改变 { ... } //类C的while循环 for 条件表达式 { ... } //无限循环 for{ ... } //类C的for each for k, v := range R {//R可为数组、切片、map、字符串等 ... }
都用来直接跳转到指定标签处继续向下执行。Delphi
须要先使用label
声明标签,Go
则直接使用标签,标签的语法均为:
labelA: ...
都用来结束本次循环并进入下一次循环。在多层循环中,Go
还能够在其后跟标签,用来结束标签对应的那层循环并进入下一次循环。
都用来结束当前循环并执行当前循环以后的代码。在多层循环中,Go
还能够在其后跟标签,用来结束标签对应的那层循环并执行循环以后的代码。
Delphi
//过程,无返回值 procedure Proc1; begin ... end; procedure Proc2(i1, i2: Integer; s1, s2: string); begin ... Proc1; end; //函数,有返回值 function Func1: string; begin ... Result := '返回值'; Proc2(1, 2, 'abc', 'def'); end; function Func2(i1, i2: Integer; s1, s2: string): string; begin ... Result := Func1(); end;
Go
//无返回值 func f1() { ... } func f2(i1, i2 int, s1, s2 string) { ... f1() } //有返回值 func f3() string { r := "返回值" return r //必须带 r } func f4() (s string) { s = f3() return s //可省略 s } func f5() (r1 int, r2 string) { r1, r2 = f6(1, 2, 'abc', 'def') return r1, r2 //可省略 r1, r2,若不省略,则顺序必须一致 } func f6(i1, i2 int, s1, s2 string) (r1 int, r2 string) { r1 = i1 + i2 r2 = s1 + s2 return }
Delphi
无返回值的用procedure
,有返回值的用function
;Go
统一用func
。
都接受0个或多个参数,Delphi
同类型参数间用,
分隔,不一样类型参数间用;
分隔;Go
统一用,
分隔。
Delphi
函数只能有1个返回值,且返回值固定由Result
隐藏变量携带,Result
可屡次赋值,以后也可有其它语句;Go
能够有0个或多个返回值,返回值变量能够声明也能够不声明,但必须由return
返回且必须为最后一条语句,若返回值变量有多个且已声明,return
后跟的返回值变量顺序必须与定义一致。
Delphi
无参过程/函数调用时,括号()
可带可不带;Go
无参函数调用时必须带()
。
Delphi
被调用的过程/函数必须在主调过程/函数以前实现或在interface
区已声明;Go
函数没有声明只有实现,且不要求被调函数在主调函数以前定义。
如下概念比较庞杂,三言两语难以说清楚,暂且告一段落