zoukankan      html  css  js  c++  java
  • Go基础学习

    官方文档:https://golang.google.cn/doc/

    中文文档:https://www.topgoer.cn/docs/golang

    Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。

    对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了

    go语言的优点:

      自带gc,自动垃圾回收

      静态编译,编译好后,扔服务器直接运行

      简单的思想,没有继承,多态,类等

      丰富的库和详细的开发文档

      速度快。几乎和C一样快

    go适合做什么:

      服务端开发

      分布式系统,微服务

      云平台

    GOROOT和GOPATH的区别:

      GOROOT是Go的安装路径

      GOPATH:

        GOPATH是一个环境变量,用来表明你写的go项目的存放路径。

        GOPATH路径最好只设置一个,所有的项目代码都放到GOPATH的src目录下。

        在进行Go语言开发的时候,我们的代码总是会保存在$GOPATH/src目录下。在工程经过go build、go install或go get等指令后,会将下载的第三方包源代码文件放在$GOPATH/src目录下, 产生的二进制可执行文件放在 $GOPATH/bin目录下,生成的中间缓存文件会被保存在 $GOPATH/pkg 下

        (类似于maven仓库+工程目录)

        1)存放sdk以外的第三方类库

        2)自己收藏的可复用的代码

        3)自己项目的源代码

    # 配置GOROOT
    export GOROOT=/usr/local/go
    # 配置GOPATH
    export GOPATH=/Users/yangyongjie/GoProjects
    # 配置GOBIN
    export GOBIN=$GOPATH/bin
    # 配置PATH,mac多个之间:分隔
    export PATH=$PATH:$GOROOT/bin:$GOBIN

        编辑 ~/.bash_profile 或者 /etc/profile 添加以上配置,保存然后执行 source ~/.bash_profile 或 source /etc/profile 使配置立即生效即可

      Go基础:

       Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。

       Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)

      1、环境安装

        安装包下载地址为:https://golang.org/dl/。如果打不开可以使用这个地址:https://golang.google.cn/dl/

        截止2021年底,建议下载安装Go 1.16.12 版本

      2、HelloWorld

    package main   // 包声明,package main表示一个可独立执行的程序,每个Go应用程序都包含一个名为main的包
    
    import "fmt"   // 引入包,引入多个包使用()括起来,引用其他go文件,也需要导入其所在包名
    
    func main() {  // 程序开始执行的函数,每一个可执行的程序必须包含main函数。注意,{ 不能单独放在一行
       /* 这是我的第一个简单的程序 */
       fmt.Println("Hello, World!")
    }

      执行:

        1)右键run即可。

        2)或者将上面代码保存为hello.go,然后执行 go run hello.go

        3)使用 go build命令生成二进制文件,然后执行二进制文件。 go build hello.go  ./hello

         

     Go语法:

      命名:

        1)文件命名

          小写单词,下划线分割各个单词

        2)包名称

          保持包的名字和目录一致,尽量采取有意义的包名,简短,有意义,尽量和标准库不要冲突。

          包名应该为小写单词,不要使用下划线或者混合大小写,不能包含-等特殊符号

        3)结构体、变量、函数、方法、接口等的命名

          ①:需要使用驼峰命名法,且不能出现下划线

          ②:根据首字母的大小写来确定可以访问的权限。无论是方法名、常量、变量名还是结构体、接口的名称,如果首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用

            可以简单的理解成:首字母大写是公有的,包外可访问;首字母小写是私有的,仅在包内可访问

          ③:变量为bool型,则名称应以 Is,Has、Can、Allow等开头

            接口以 er 作为后缀

            常量均需使用全部大写字母组成,并使用下划线分词

            单元测试文件名命名规范为 example_test.go 测试用例的函数名称必须以 Test 开头

      自定义包:

        我们创建go文件时,可以自定义其包名,不过为了便于维护,建议与文件夹名称一致

        一个包中可以有任意多个文件,文件的名字也没有任何规定(但后缀必须是 .go)

        一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下

        要调用其他go文件中的函数,必须导入其所在的包

        对引用自定义包需要注意以下几点:

          ①:如果项目的目录不在 GOPATH 环境变量中,则需要把项目移到 GOPATH 所在的目录中,或者将项目所在的目录设置到 GOPATH 环境变量中,否则无法完成编译

          ②:使用 import 语句导入包时,使用的是包所属文件夹的名称

          ③:包中的函数要在外部被调用的话,则函数名第一个字母要大写,否则无法在外部调用

          ④:自定义包的包名不必与其所在文件夹的名称保持一致,但为了便于维护,建议保持一致

          ⑤:调用自定义包时使用 包名 . 函数名 的方式,如上例:demo.PrintStr()

     

      1、注释

        // 单行注释

        /* 多行注释 */ 

      2、数据类型    

    序号类型和描述
    1 布尔型
    布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
    2

    数字类型
    整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。

    Go语言中没有字符类型,字符只是整数的特殊用例。因为用于表示字符的byte和rune(字符默认类型)类型分别等于int8和int32。Go中字符用单引号('')包围

    3

    字符串类型:
    字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 字符串的值为双引号("")中的内容

    字符串可以使用+拼接,fmt.Sprintf 格式化字符串并赋值给新串

    4 派生类型:
    包括:
    • (a) 指针类型(Pointer)
    • (b) 数组类型
    • (c) 结构化类型(struct)
    • (d) Channel 类型
    • (e) 函数类型
    • (f) 切片类型
    • (g) 接口类型(interface)
    • (h) Map 类型

         

         值类型:

        bool
        int(32 or 64), int8, int16, int32, int64
        uint(32 or 64), uint8(byte), uint16, uint32, uint64
        float32, float64
        string
        complex64, complex128
        array    -- 固定长度的数组

         引用类型:

        slice   -- 序列数组(最常用)
        map     -- 映射
        chan    -- 管道

          数字类型:    

    类型和描述大小(字节)对应Java类型
    uint8
    无符号 8 位整型 (0 到 255)
    1 -
    uint16
    无符号 16 位整型 (0 到 65535)
    2 -
    uint32
    无符号 32 位整型 (0 到 4294967295)
    4 -
    uint64
    无符号 64 位整型 (0 到 18446744073709551615)
    8 -
    int8
    有符号 8 位整型 (-128 到 127)
    1 byte
    int16
    有符号 16 位整型 (-32768 到 32767)
    2 short
    int32
    有符号 32 位整型 (-2147483648 到 2147483647)
    4 int
    int64
    有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
    8 long

          浮点型:

    类型和描述
    float32
    IEEE-754 32位浮点型数
    float64
    IEEE-754 64位浮点型数
    complex64
    32 位实数和虚数
    complex128
    64 位实数和虚数

          其他类型:

    类型和描述
    byte
    type byte = uint8
    rune
    type rune = int32
    uint
    32 或 64 位
    int
    与 uint 一样大小
    uintptr
    无符号整型,用于存放一个指针

      Go类型转换:

        type_name(expression)

        type_name 为要转换成的类型,expression 为表达式

      3、变量

        Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。

        声明变量一般使用var关键字:var identifier1,identifier2 type

        声明变量没有初始化值,则为系统默认设置的值:

          数值类型(包括complex64/128)为 0

          布尔类型为 false

          字符串为 ""(空字符串)

          以下几种类型为 nil:    

    var a *int
    var a []int
    var a map[string] int
    var a chan int
    var a func(string) int
    var a error // error 是接口

        还可以使用 := 直接声明并初始化变量,编译器根据值自动判断类型(使用变量的首选方式)。如:  identifier := 123 

        不过:= 这种方式只能被用在函数体内,而不能用于全局变量的声明和赋值。

          如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误

        

        值类型和引用类型:

          值类型:所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值,

              当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝

               可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。值类型的变量的值存储在栈中

          引用类型:一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。

               这个内存地址称之为指针,这个指针实际上也被存在另外的某一个值中

               当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容

      4、常量

        常量是一个简单值的标识符,在程序运行时,不会被修改的量。

        常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

        常量不能用 := 语法声明

        常量的定义格式:

          const identifier [type] = value

        可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。

          显式类型定义: const b string = "abc"

          隐式类型定义: const b = "abc"      

        多个相同类型的声明可以简写为:

          const c_name1, c_name2 = value1, value2

        const x string = "abc"
        const y = "abc"
        fmt.Println(x, y)
    
        // 多个常量也可以一起声明,常量还可以用作枚举:
        const (
            Unknown = 0
            Female  = 1
            Male    = 2
        )

        iota,特殊常量,可以认为是一个可以被编译器修改的常量。
          iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。 使用iota能简化定义,在定义枚举时很有用。

      5、运算符

        Go语言没有三目运算符

        1)算术运算符

          下表列出了所有Go语言的算术运算符。假定 A 值为 10,B 值为 20。

    运算符描述实例
    + 相加 A + B 输出结果 30
    - 相减 A - B 输出结果 -10
    * 相乘 A * B 输出结果 200
    / 相除 B / A 输出结果 2
    % 求余 B % A 输出结果 0
    ++ 自增 A++ 输出结果 11
    -- 自减 A-- 输出结果 9

        2)关系运算符

    运算符描述实例
    == 检查两个值是否相等,如果相等返回 True 否则返回 False。 (A == B) 为 False
    != 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 (A != B) 为 True
    > 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 (A > B) 为 False
    < 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 (A < B) 为 True
    >= 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 (A >= B) 为 False
    <= 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 (A <= B) 为 True

        3)逻辑运算符

          下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。

    运算符描述实例
    && 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 (A && B) 为 False
    || 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 (A || B) 为 True
    ! 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 !(A && B) 为 True

        4)位运算符

          Go 语言支持的位运算符如下表所示。假定 A 为60,B 为13:

    运算符描述实例
    & 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 (A & B) 结果为 12, 二进制为 0000 1100
    | 按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或 (A | B) 结果为 61, 二进制为 0011 1101
    ^ 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (A ^ B) 结果为 49, 二进制为 0011 0001
    << 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。 A << 2 结果为 240 ,二进制为 1111 0000
    >> 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。

        5)赋值运算符

    运算符描述实例
    = 简单的赋值运算符,将一个表达式的值赋给一个左值 C = A + B 将 A + B 表达式结果赋值给 C
    += 相加后再赋值 C += A 等于 C = C + A
    -= 相减后再赋值 C -= A 等于 C = C - A
    *= 相乘后再赋值 C *= A 等于 C = C * A
    /= 相除后再赋值 C /= A 等于 C = C / A
    %= 求余后再赋值 C %= A 等于 C = C % A
    <<= 左移后赋值 C <<= 2 等于 C = C << 2
    >>= 右移后赋值 C >>= 2 等于 C = C >> 2
    &= 按位与后赋值 C &= 2 等于 C = C & 2
    ^= 按位异或后赋值 C ^= 2 等于 C = C ^ 2
    |= 按位或后赋值 C |= 2 等于 C = C | 2

        6)其他运算符

    运算符描述实例
    & 返回变量存储地址 &a; 将给出变量的实际地址。
    * 指针变量。 *a; 是一个指针变量

        运算符优先级:

          有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:

    优先级运算符
    5 * / % << >> & &^
    4 + - | ^
    3 == != < <= > >=
    2 &&
    1 ||

          当然,你可以通过使用括号来临时提升某个表达式的整体运算优先级

      6、条件语句

        1)if语句

        和Java中的循环语句基本相同,if后的条件不加括号,也可以加括号

    if 布尔表达式1 {
       /* 在布尔表达式1为 true 时执行 */
    } else  if  布尔表达式2{
      /* 在布尔表达式2为 true 时执行 */
    } else {
      /* 在布尔表达式都为 false 时执行 */
    }

        2)switch

          switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止

          switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。

          switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough

    switch var1 {
        case val1:
            ...
        case val2:
            ...
        default:
            ...
    }

      

      7、循环语句

        go只有一种for循环

        for init;condition;post{} 和Java中的for(int i=0;i<10;i++){} 相同
          1、先对表达式1赋值
          2、判别赋值表达式init是否满足给定条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行post,进入第二次循环,再判别condition
          3、若判断condition条件不满足,就终止for循环,执行循环体外语句

        条件循环:

          for condition { } 和Java中的while(condition){} 一样

        无限循环:

          for{...} 和Java中的for(;;){} 一样,即while(true){}

          或 for true {...}

        For-each range循环:

          可以对字符串、数组、切片等进行迭代输出元素

          for i,x:=rang numbers{} i表示索引(从0开始),x表示索引的值    

    dataType 1st value 2nd value  
    string index s[index]

    unicode, rune

    (range遍历得到的是rune类型的字符)

    array/slice index s[index]  
    map key value(m[key])  
    channel element    

          可忽略不想要的返回值,使用 "_" 这个特殊变量

            另外,引用类型如map的遍历,for range 循环的时候会创建每个元素的副本,而不是元素的引用      

            需要注意:基本类型时range会复制对象,然后再进行迭代,迭代的元素都是从复制对象中输出的。使用引用类型则不会

          

          for-range 和 for 循环的区别:

            for-range 遍历会在遍历之前,先拷贝一份被遍历的数据,然后遍历拷贝的数据;for则不会

            所以在for-range 遍历过程中去修改被遍历的数据,只是修改拷贝的数据,不会影响到原数据。

            同时,因为for-range 会拷贝被遍历数据,因此在需要遍历内存占用比较大的数组时,建议使用普通遍历。如果必须使用范围遍历,我们可以遍历数组的地址或先将数组转换为切片(引用类型底层数据不会被复制),然后遍历。

        // 死循环
        for {
            fmt.Println("123")
        }
    
        for i := 0; i < 10; i++ {
            fmt.Println(i)
        }
    
        // for condition 类似于while(condition)
        j := 0
        for j < 10 {
            fmt.Println(j)
            j++
        }
        fmt.Println(j)
    
        // 声明切片
        intSlice := []int{1, 2, 3, 4, 5}
        // for each循环
        for k, n := range intSlice {
            fmt.Println(k, n)
        }

         

        循环控制语句:

          break:用于中断当前for循环或跳出switch语句

          continue:跳过当前循环的剩余语句,然后继续下一轮循环

          goto:将控制转移到被标记的语句,如连续跳出两层循环:

        LOOP:
            for j, ch := range row {
                // 如果当前元素的值为0
                if ch != 0 {
                    continue
                }
                // 其一整行是否都为0
                for k := 0; k < n; k++ {
                    if trust[k][j] != 0 {
                        goto LOOP
                    }
                }
            }

      8、变量作用域

        作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。

        Go 语言中变量可以在三个地方声明:

          函数内定义的变量称为局部变量(作用域只在函数体内,参数和返回值也是局部变量)
          函数外定义的变量称为全局变量(可以在整个包甚至是外部包(被导出后)使用)
          函数定义中的变量称为形式参数

      9、数组

        1)声明

          var var_name [size] var_type 如:var a [10] int32

          声明并初始化:var a = [5] int{1,2,3,4,5} ,初始化数组中 {} 中的元素个数不能大于 [] 中的数字

          如果数组长度不固定,可以使用 ... 代替,编译期会根据元素个数自行推断数组的长度,如: var arr = [...]int{1, 2, 3, 4, 5}

          如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小

        2)访问
          根据索引(从0开始)来读取值:var ar = arr[2]

        3)向函数传递数组 

    void myFunction(param []int)
    {...}

        

      10、切片(Slice,"动态数组")

        Go语言切片是对数组的抽象(一般使用切片代替数组就行了)

        Go数组的长度不可改变,在特定场景中这样的集合就不太适用。为此Go提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

        1)定义切片(与数组定义类似,[]中省略了长度)

          var identifier []type

          切片不需要说明长度

          或使用make()函数来创建切片:

    var slice1 []type = make([]type, len)
    // 也可以简写为
    slice1 := make([]type, len) // len表示数组的长度并且也是切片的初始长度

        2)切片初始化

    s :=[] int {1,2,3 }   // 直接初始化,[]表示是切片类型
    s:= arr[:]    初始化切片s,是数组arr[:]的引用
    s:= arr[startIndex:endIndex]        将 arr中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片(包左不包右)
    s:= arr[startIndex:]        默认 endIndex 时将表示一直到arr的最后一个元素
    s:= arr[:endIndex]        默认 startIndex 时将表示从 arr 的第一个元素开始
    s := s1[startIndex:endIndex]   // 根据切片s1初始化
    s :=make([]int,len,cap)           // 通过内置函数 make() 初始化切片s

        3)切片内置函数

          len():获取切片长度

          cap():获取切片最大可达长度

          append():向切片中追加元素

          copy():拷贝另一个切片的元素,如:/* 拷贝 numbers 的内容到 numbers1 */ copy(numbers1,numbers)

        4)切片截取

          可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound]

     /* 创建切片 */
    numbers := []int{0,1,2,3,4,5,6,7,8}  
    // 截取切片从索引1(包含) 到索引4(不包含)
    numbers[1:4]
    // 默认下限为0
    numbers[:3]
    // 默认上限为len(s)
    numbers[4:]

        5)对切片数据排序 

        s := []int{1, 2, 3, 6, 7, 9, 3}
        // sort.Ints对整数进行排序,sort.Strings对字符串进行排序,sort.Float64s对浮点数进行排序,sort.Sort对接口类型排序
        sort.Ints(s)
        for _, v := range s {
            fmt.Print(v) // 1233679
        }

        5)切片遍历

        s := []string{"beijing", "shanghai", "guangzhou", "shenzheng"}
        
        // 普通for循环遍历
        for i:=0;i<len(s);i++{
             fmt.Printf(s[i])
        }
        // range循环遍历
        for i, v := range s {
            fmt.Printf("索引 %d 的值 = %s\n", i, v)
        }

      11、Map(集合)

        Map是一种无序的键值对的集合。

        Map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值

        Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的

        1)Map定义

    /* 声明变量,默认 map 是 nil ,nil map 不能用来存放键值对*/
    var map_variable map[key_data_type]value_data_type
    如:var paramMap map[string]string
    
    /* 必须使用 make 函数分配内存后才能用来存放键值对 */
    map_variable := make(map[key_data_type]value_data_type)
    如:map_name:=make(map[string]string)

        2)Map操作

    // map中插入键值对:
    map_name[key_value]= value
    
    // 遍历map
    for k,v := range map_name {}
    
    // 遍历Map,只遍历key
    for key:=range map_name {}
    
    // 判断map中是否包含某个key
    value,ok:=map_name[key_name]
    存在:if ok {}
    
    // 删除集合中的元素
    delete()函数,用于删除集合中的元素,参数为map和其对应的key
    delete(map_name,key_name)

       3)sync.Map

        Map不是线程安全的,并发安全的map使用 sync.Map

        // 线程安全的Map
        safeMap := &sync.Map{}
        // 插入键值对
        safeMap.Store("name", "yangyongjie")
        safeMap.Store("age", "20")
        safeMap.Store("city", "nanjing")
        // 根据key获取value
        value1, ok := safeMap.Load("name")
        fmt.Println(value1)
        // 如果key存在,则返回value;如果不存在,则插入给定的value
        value2, ok := safeMap.LoadOrStore("age", 27)
        fmt.Println(value2)
        // 删除key
        safeMap.Delete("name")
        // 遍历
        safeMap.Range(func(key, value interface{}) bool {
            fmt.Println(key)
            fmt.Println(value)
            return true
        })

      12、其他

        1)下划线

          “_”是特殊标识符,用来忽略结果

          ①:下划线在import中

             import 下划线(如:import hello/imp)的作用:当导入一个包时,该包下的文件里所有init()函数都会被执行,然而,有些时候我们并不需要把整个包都导入进来,仅仅是是希望它执行init()函数而已。这个时候就可以使用 import 引用该包。即使用【import _ 包路径】只是引用该包,仅仅是为了调用init()函数,所以无法通过包名来调用包中的其他函数

          ②:下划线在代码中

             忽略这个变量,意思是把值赋给下划线,丢掉不要

        for _, c := range s {
            // 忽略索引这个变量
        }

            

    END.

  • 相关阅读:
    C#常用笔记.cs
    OpenFileDialog 和 FolderBrowserDialog
    C#双缓存.cs
    数据库设计范式
    js 给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果
    Study Plan The Sixth Day
    Study Plan The First Day
    Study Plan The Seventh Day
    Study Plan The Second Day
    C#CustomAttribute和泛型约束 应用于经典数据处理适配
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/15061344.html
Copyright © 2011-2022 走看看