从Delphi到Go——基础

废话

长期从事Delphi开发,虽不敢说精通,但说很熟悉仍是至关有自信的。不过,只会一门语言,并且仍是这么老的语言,更是在大天朝很小众的语言,总感受本身离饿死街头没多远了,因此趁着还没老再学个潮点的吧。c++

先前考虑过Python,初步了解后以为不太适合本身:编程

  1. 解释型语言:部署时得先搞个运行环境,发布的程序就是源码自己,再加上这个执行效率,怎么想都仍是编译型语言更合适。数组

  2. 动态语言:无需声明,拿来就用,这已经很不合习惯了。想一想一个变量,前一秒仍是浮点数,下一秒就成字符串了,再一眨眼又成某个对象了……虽然通常不会有人这么写,可是挡不住手误啊,仍是把这种小细节交给编译器更让人放心。并发

因此,对于有点强迫症洁癖的本身,最后仍是选了Go,比较符合已有的编程习惯,学习成本应该相对会低些吧。ide

至于Go嘛,想学是已经好久了,但因为种种缘由却迟迟未开启,不过终究仍是要迈出这一步的,因此就搞这么个系列来记录吧,一方面算是自我督促,另外一方面也算是一种交流吧,固然,若一不留神帮上了谁,那自是开心极了。函数

言归正传

已经初步了解过了Go,说来和Delphi仍是有很多类似之处呢,从Delphi转向Go应该会比较轻松吧。学习

工程结构

Delphi的工程算是比较自由的,源码的话,只要把单元路径引了或是直接包含进工程单元里就能够了,编译出的dcu和最终的exe指定下路径也就没问题了,一般我都使用下面这种结构:测试

Project/
  bin/
  src/
    dcu/
    mod1/
      *.dfm
      *.pas
    mod2/
      *.dfm
      *.pas
    *.dpr

不过,每个工程都要设置,并且我习惯将DebugRelease设置彻底同样,也还真是够烦的。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

  • 赋值运算符::=
  • 算术运算符:+-*/divmod
  • 逻辑运算符:notandorxor
  • 位运算符:notandorxorshlshr
  • 关系运算符:=<>><>=<=
  • 指针运算符:^+-=<>
  • 地址运算符:@
  • 字符串链接符:+
  • 集合运算符:+-*<=>===<>in
  • 类运算符:asis=<>

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还多了一些特性:

  1. 支持类型推导,声明并初始化时可省略类型。

  2. 在函数内部声明并初始化变量时还支持用:=的方式简写(不过这和Delphi的赋值符号同样,意义却截然不同,使用时得留意了)。

  3. 函数内部的变量能够像c++同样达到语块级的做用域和生命期。

  4. 支持多重赋值,交换变量更简洁。

  5. 支持匿名变量,即用_来占位。

常量

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没有枚举的关键字,但使用constiota也能够达到目的。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、字符串等
  ...
}

控制语句

goto

都用来直接跳转到指定标签处继续向下执行。Delphi须要先使用label声明标签,Go则直接使用标签,标签的语法均为:

labelA:
  ...

continue

都用来结束本次循环并进入下一次循环。在多层循环中,Go还能够在其后跟标签,用来结束标签对应的那层循环并进入下一次循环。

break

都用来结束当前循环并执行当前循环以后的代码。在多层循环中,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
}
  1. Delphi无返回值的用procedure,有返回值的用functionGo统一用func

  2. 都接受0个或多个参数,Delphi同类型参数间用,分隔,不一样类型参数间用;分隔;Go统一用,分隔。

  3. Delphi函数只能有1个返回值,且返回值固定由Result隐藏变量携带,Result可屡次赋值,以后也可有其它语句;Go能够有0个或多个返回值,返回值变量能够声明也能够不声明,但必须由return返回且必须为最后一条语句,若返回值变量有多个且已声明,return后跟的返回值变量顺序必须与定义一致。

  4. Delphi无参过程/函数调用时,括号()可带可不带;Go无参函数调用时必须带()

  5. Delphi被调用的过程/函数必须在主调过程/函数以前实现或在interface区已声明;Go函数没有声明只有实现,且不要求被调函数在主调函数以前定义。


如下概念比较庞杂,三言两语难以说清楚,暂且告一段落

错误处理

接口

面向对象

并发

相关文章
相关标签/搜索