C# -> Golang

# 目次

# まえがき
C#使いがGo言語に移行するときに役立ててほしいと思い書きました。

C#についての詳しい説明は割愛します。

# 言語概要

- Google が開発したプログラミング言語です。「**Go言語**」や「**Golang**」と表記されます。
- UNIX、B言語(C言語の元)、UTF-8の開発者ケン・トンプソンや、UNIX、Plan 9、UTF-8の開発者ロブ・パイクによって設計されました。
- 静的型付け、メモリ安全性、ガベージコレクションを備えるコンパイル言語です。
- シンプル、高速、メモリ効率が良い、メモリ破壊が無い、並行処理が得意などの特徴を備えています。
- メモリ破壊が無く、並行処理を得意とする、進化したC言語という側面があります。
- Linux、Mac OS X、Windows、Android、iOS で動作します。

# 言語仕様

### セミコロン

C系統の言語と違い、基本的にセミコロンは必要ない

```csharp:C#
int hoge = 123;
int fuga = 456;
```

```go:Go
// セミコロンは各行に書く必要は無いが、
// 書いてもコンパイル自体に影響はない
var hoge int = 123;
// 次のように、一行で記述する際
// 区切りとして利用する場合がある
hoge := 123; fuga := 456
```

### インデント

C++と同じインデント

インデントや波括弧の位置を正しく記述しないと、構文として認識されずにコンパイルエラーが起こる場合がある

```csharp:C#
public static void Main()
{
	Console.WriteLine("Hello World");
}
```

```go:Go
func main() {
	fmt.Println("Hello World")
}
```

### 未使用の変数

Go言語では未使用の変数があると、コンパイルエラーが起こる

### ブランク変数 **_**

宣言する必要はあるが、値を使いたくない場合には

ブランク変数 **_** を利用することで、未使用でもコンパイルエラーが起こらない

```go:Go
var hoge = 10 // 未使用だとコンパイルエラー
var _ = 0 // 未使用でも問題ない
```

### 命名規則

言語仕様による命名規則として、

小文字からスタートするのは private

大文字からスタートするのは public

となっている

# インポート

C#でいうusing

```csharp:C#
using System;
using UnityEngine;
```

```go:Go
// 文字列で指定する
import "fmt"
// 括弧で括ると一気にimportできる
import (
// 改行の際に何もいらない
	"os"
	"fmt"
)
```

# 型

C#とだいたい同じ

```go
bool				真偽値(true or false)
int8/int16/int32/int64		nビット整数
uint8/uint16/uint32/uint64	nビット非負整数
float32/float64		nビット浮動小数点数
complex64/complex128		nビット虚数
byte				1バイトデータ(uint8と同義)
rune				1文字(int32と同義)
uint				uint32 または uint64
int				int32 または int64
uintptr				ポインタを表現するのに十分な非負整数
string				文字列
```

C#との大きな違い

**float**

float32 / float64 と明示的に書く必要がある (int型は必要ない)

**rune**

char型とは中身が微弱に違う

**string**

C#のようにstring[n] で1文字指定して取得することはできない

Goの場合、string[n]だとbyte単位、for文で取り出すとrune単位

# 変数宣言

Go言語は後置型宣言言語なので構文が特殊です。

**varステートメント** を変数 (Variables**)** 宣言に利用します。

```csharp:C#
int hoge = 123;
// C#での型推論 見やすいね
var piyo = 456;
```

```go:Go
// 省略無しの変数宣言(初期値は0)
var hoge int = 123
// 初期値により型名が明白な場合、
// 型名を省略できる
var hoge2 = 123
// 初期値を指定する場合に = ではなく、
// := を用いるとvarも省略可能 
hoge3 := 123
```

### 一括変数宣言と代入

```csharp:C#
// C#は , 区切りで一括宣言を行える
int a = 1, b =3;
// 同じ値を代入する場合は=で繋げることができる
a = b = 3;

// 複数個の変数に対しても型名の省略と
// 代入を行える
var (piyo, piyo2, piyo3) = (1, 2, 3);
```

```go:Go
// 丸括弧で括ると一括で宣言を行える
var (
	hoge int
	hoge2 int = 456
	// ここで初期値も指定しているので、
	// 型名も省略できる
	hoge3 = 789
)

// 複数個の変数に対しても型名の省略と
// 代入を行える
piyo, piyo2, piyo3 := 1, 2, 3
```

### 定数 (const)

```csharp:C#
// C#のconst
const int hoge = 334;

// c# 10.0 からは文字列補完でも、
// {}の中身がconst文字列の場合に限り、
// 補完結果もconstにできる
const string hoge = "hoge";
const string fuga = "fuga";
const string piyo = $"{hoge} {fuga}";
// piyoには "hoge fuga" が代入されている

// 次のように中身が文字列ではない場合エラーになる
const int hage1 = 334;
const string hage2 = $"{hage1}"; // エラー
```

```go:Go
// 型無し定数 (untyped constant)
// 指定された値によって型推論する
const hoge = 334
// 明示的に型を指定していないので
// 次のように異なるデータ型変数に代入できる
var fuga int = hoge
var piyo float64 = hoge

// 複数の定数宣言も行える
const (
	hage1 = 123
	hage2 = 456
)

// 型付き定数 (typed constant)
// 型を明示的に指定して宣言する
const hoge int = 334
// 型が明示されているので
// 同じ型の変数にしか代入できない
var fuga int = hoge // これはOK
var piyo float64 = hoge // エラー
// 代入する場合には明示的なキャストが必要
var piyo float64 = float64(hoge)

// 複数の宣言でも型無し定数と
// 型付き定数を宣言できる
const (
	hoge = 0
	piyo int = 1
)
```

### (Goのみ)定数への値の自動割り当て

定数の宣言時に **iotaキーワード** を割り当てると、0から順番に数値の連番を割り当てる

```go:Go
const (
	hoge = iota // 0
	fuga // 1
	piyo // 2
	hage // 3
)
// iotaに値を割り当てると、その数基準に連番となる
const (
	hoge2 = iota + 10 // 10
	fuga2 // 11
	piyo2 // 12
	hage2 // 13
)
```

# 標準入出力

Go言語では標準入出力の種類が多め

### 標準入力 (fmt ライブラリ)

```go:Go
// 改行またはスペースまで読み込む
// 型は事前に定義しておく
var str string
fmt.Scan(&str)
// スペース区切りの数が分かっている入力を受け取る
var arr [3]int
fmt.Scan(&arr[0], &arr[1], &arr[2], &arr[3])
```

### 標準入力 (bufio ライブラリ)

```go:Go
// 入力はstring型で受け取る
// 標準入力を受け付けるスキャナ
var scanner *bufio.Scanner = bufio.NewScanner(os.Stdin)
// 1行分のスキャンを行い、スキャナ内に格納する
scanner.Scan()
// スペース区切りで分割する
var inputs []string = strings.Split(scanner.Text(), " ")
```

### 標準出力 (fmt ライブラリ)

```go:Go
var name string = "hoge"
var age int = 24
// 改行無しで与えられた引数をスペース区切りで標準書式(%v)で出力
fmt.Print("Name:", name, " Age:", age) // 出力: Name:hoge Age:24
// 改行有りで与えられた引数をスペース区切りで標準書式で出力
fmt.Println("Name:", name, "Age:", age) // 出力: Name:hoge Age:24 (\n)
// 書式指定子を利用して、値を出力する
fmt.Printf("Name: %s Age: %d", name, age) // 出力: Name: hoge Age: 24
```

# 明示的なキャスト

Go言語はキャストする際に変数名を丸括弧でくくる

```csharp:C#
int hoge = 123;
float fuga = (float)hoge;
```

```go:Go
var hoge int = 123
var fuga float64 = float64(hoge)
```

# 配列 (array)

Go言語における配列とは、コンパイル時にサイズが決まっていて変更不可のもの

途中でサイズを変更することはできない

```csharp:C#
int[] hoge = new int[n];
var hoge = new int[n];
int[] hoge = new[] {n1, n2, n3};
int[] hoge = {n1, n2, n3};
// C# 12からはコレクション式が利用できる
int[] hoge = [n1, n2, n3];
```

```go:Go
// 型名の前にサイズを指定する
var hoge [n]int = [n]int{}
// 左辺の型名を省略する場合、
// サイズ指定は右辺のみ
var hoge = [n]int{}
hoge := [n]int{}

// サイズに...を指定すると、
// 初期値に指定された値の数によって
// 配列のサイズが決まる
hoge := [...]int{1, 2}
// 次のように:を使用すると
// n番目の要素に1を指定できる
// それ以外の要素は0
hoge := [...]int{n: 1}
```

使い方はどちらも同じ

### スライス (slice) C#でいうListに近い

Go言語において、厳密ではないがイメージとして

サイズが固定のもの → **配列**

サイズが可変のもの → **スライス**

と考えると良いかもしれない

```csharp:C#
List<int> hoge = new List<int>();
var hoge = new List<int>();

// C# 12からはコレクション式が利用できる
List<int> hoge = [];

// 要素追加
hoge.Add(334);
```

```go:Go
// 宣言自体は配列とほぼ同じ
// サイズは指定しない
var hoge []int = []int {}
var hoge = []int{}
var hoge := []int{}
// スライスは配列への参照型で値を持つので、
// 配列から部分列を取り出して作成することもできる
hoge := [...] string{"piyo", "piyo2"}
fuga := hoge[0:2] // fuga = [piyo piyo2]
// 上記の操作は次の指定方法がある
arrName[n1:n2] // n1 から n2 - 1まで
arrName[n:] // n から末尾まで
arrName[:n] // 先頭から n - 1 まで
arrName[:] // 先頭から末尾まで
// 要素の追加は
// append(追加するスライス, 追加する要素)
newHoge = append(hoge, n)
// appendとは末尾に要素を追加した新しいスライスを返す
```

<aside>
💡 **スライスは配列への参照型で値を持つ**

</aside>

### 要素数の確認

```csharp:C#
List<int> hoge = [1, 2, 3];
hoge.Count;

int[] hoge = [1, 2, 3];
hoge.length;
```

```go:Go
// 
arr := [...]int {1, 2, 3}
slice := arr[0:2]
// 長さ(length)はそれに含まれる要素数
// 容量(capacity)はスライスの最初の要素
// から数えて、元となる配列の要素数
len(slice) // sliceの要素数は2
cap(slice) // sliceの生成元の要素数は3
```

### サイズを指定しての宣言

```csharp:C#
// 第一引数 n 容量
List<int> hoge = new List<int>(n)
```

```go:Go
// make()関数は
// 第一引数 []T 型
// 第二引数 len 長さ
// 第三引数 cap 容量
hoge := make([]int, n1, n2)
```

スライス (List) は容量超過時に倍のメモリを確保し要素をコピーするので、

宣言時に容量をあらかじめ確保しておくことで、超過時のメモリ再確保を減らして速度を高めることができる

スライスの初期値

null ではなく、**nil**

### マップ(map) C#でいうDictionary<T>

```csharp:C#
Dictionary<string, int> hoge = new()
{
    {"x", 100},
    {"y", 200}
};
```

```go:Go
// map[keyの型]valueの型で宣言し、
// 要素の初期化は : 区切りで行う
hoge := map[string]int {
	"x": 100,
	"y": 200, // 末尾の , は省略できない
}
// マップに要素追加
hoge["addKeyName"] = value
// マップの要素削除
delete(hoge, "keyName")
// マップの長さを求める
len(hoge)
// マップに要素が存在する
_, flag := hoge["searchKeyName"]
```

# If 文

インデントと括弧にさえ気を付ければ、どちらも同じ

```csharp:C#
if (hoge == true)
{
	Console.WriteLine("hoge");
}
else if (fuga == false)
{
	Console.WriteLine("fuga");
}
else
{
	Console.WriteLine("piyo");
}
```

```go:Go
// 条件に括弧は必要ない
if hoge == true {
	fmt.Println("hoge")
// if文を続ける際に下記の通りにインデント、
// 改行をしないとコンパイルエラー
} else if fuga == false {
	fmt.Println("fuga")
} else {
	fmt.Println("piyo")
}
```

# Switch文

基本的なSwitch文のみ扱う

パターンマッチングなどは扱わない

```csharp:C#
int hoge = 334;
switch (hoge)
{
	case 3:
		Console.WriteLine("hoge");
		break;
	case 34:
		Console.WriteLine("fuga");
		break;
	// 上記のcaseの条件に当てはまらない場合
	// default内の処理が実行される
	default:
		Console.WriteLine("piyo");
		break;
}

```

```go:Go
hoge := 334
// Go言語ではbreakや括弧は記述しない
// switch 式 {...} では式の値によって
// 処理を振り分ける
switch hoge {
	case 3:
		fmt.Println("hoge")
	case 34:
		fmt.Println("fuga")
	default:
		fmt.Println("piyo")
}

// switch {...} ではcase分に条件を記述できる
switch {
	case fuga < piyo:
		fmt.Println("piyo big")
	case fuga > piyo:
		fmt.Println("fuga big")
	default:
		fmt.Println("Equal")
}

// 次の処理も実行できるfallthrough句がある
switch hoge {
	case 3:
		fallthrough
	case 34:
		fmt.Println("fuga")
	default:
		fmt.Println("piyo")
}
// この場合hogeが3または34の場合に
// "fuga"が実行される
```

# While文

Go言語にそんなものは無い

# For文

通常のfor文ではどちらも同じ

括弧は必要ない

```csharp:C#
for (var i = 0; i < 10; i++)
{
	Console.WriteLine("hoge");
}
```

```go:Go
// 通常のカウントアップ式for文
for i := 0; i < 10; i++ {
	fmt.Println("hoge")
}
// 条件を省略すると無限ループ
for {
	n++
	if n > 10 {
		break
	} else if n % 2 == 1 {
		continue
	} else {
		fmt.Println("hoge")
	}
}
// C#でいうForeach文
// range句を使用して中身を取り出す
for index, item := range array {
	fmt.Printf("%d: %s\n", index, item)
}
```

# Goto文

goto文は指定したラベルにジャンプする

セミコロン以外どちらも同じ

```csharp:C#
goto LABEL; // LABEL: の位置に遷移する
// skip
// skip
// skip
// skip
LABEL:
	Console.WriteLine("hoge");
	
```

```go:Go
goto LABEL // LABEL: の位置に遷移する
// skip
// skip
// skip
// skip
LABEL:
	fmt.Println("hoge")
```

# 関数宣言

戻り値で複数の型の値を返すことができる

```csharp:C#
// 戻り値無しはvoidとする(voidは型ではない)
public void funcA()
{
}
// params を用いると可変引数を実現できる
public void funcB(params int[] x)
{
}
// 戻り値が複数の場合には括弧で括る
public (int, int) funcC(int x, int y)
{
	return (x + y, x - y);
}
```

```go:Go
// 関数宣言時に func句を使用する
// 括弧の中に引数、後ろに戻り値を指定する
func funcA(x int, y int) int {
	return x + y
}
// 戻り値が複数の場合には括弧で括る
func funcB(x int, y int)(int, int) {
	return x + y, x - y
}
// ... を用いることで可変引数を実現できる
func funcC(a int, b ... int) {
	for i, num := range b {
	}
}
// 戻り値無しの場合
// 括弧の後ろに何も記述しない
func funcD() {
}
```

# 構造体

Go言語ではクラスが無いので、構造体を使用する

構造体にはメンバ変数のみを定義し、クラスメソッドに相当する関数は関数名の前に、

 `(thisに相当する変数 *構造体名)` をつけて定義する。

```csharp:C#
public struct Person
{
    public string name;
    private int age;
    
    public void Func(string name)
    {
	    this.name = name;
    }
}
var hoge = new Person
{
	name = "hoge",
};
```

```go:Go
// 宣言時にtype句を使用
type Person struct {
	name string
  age int
}

func (p *Person) SetFunc(name string) {
	p.name = name
}

func (p *Person) GetFunc() string {
	return p.name
}
// 構造体のメンバの内、
// 大文字で始まるものは外からアクセス可能
// 小文字で始まるものは外からアクセス不可
type Person struct {
	Name string // 外部からアクセス可能
	age int // 外部からアクセス不可
}
// 構造体を使用する際のパラメータの初期化
// 順序通りに初期化
hoge := Person{"Yamada", 24}
// 変数名で初期化
fuga := Person{name: "Tanaka", age: 25}
```

type句について

構造体やインターフェース宣言時によく用いられるが、意味としては違うので気を付ける

[Goを学びたての人が誤解しがちなtypeと構造体について #golang - Qiita](https://qiita.com/tenntenn/items/45c568d43e950292bc31)

# インターフェース

インターフェースの利用方法はどちらも同じで、

命名に関しては接頭詞に **I** を付けることはない

```csharp:C#
//接頭詞の I は言語仕様ではなくあくまで推奨事項
public interface IHoge
{
	public void Hoge();
}

public class Fuga() : IHoge
{
	public void Hoge()
	{
	}
}
```

```go:Go
// インターフェース宣言
type Hoge interface {
	Hoge()
	Fuga() int
	Piyo(i int) int
}
type name struct {}
// インターフェース実装
type (n name) Hoge() {}
```

### 匿名型 (interface {}型)

匿名の型によって、型を明示的に定義しない型

```csharp:C#
// シンプルな宣言
var hoge = new
{
	Name = "hoge",
	age = 334,
};
// LINQなどで使うことが多い
```

```go:Go
// 宣言はこのように行う
func hoge(a interface {}) {}
// .(型名)でinterface{}型を他の型へ変換できる
func fuga(a interface {}) {
	fmt.Printf("%d", a.(int))
}
// 型変換の第二戻り値は型変換可能かどうかを返す
func piyo(a interface {}) {
	str, ok := a.(string)
}
```

# ポインタ (pointer)

Go言語はガベージコレクション(GC)が搭載されているので、メモリリークは基本的に起こらない

メモリ割り当て自体は、内部で自動的にヒープ領域かスタック領域に割り当てられる

これらの機能により、メモリ安全な言語である

```go:Go
// オブジェクトのポインタを参照するには &
// ポインタの中身を参照するには *
var a int // int型変数
var p *int // intへのポインタ変数
p = &a // p に a のポインタを設定
*p = 334 // ポインタの中身に代入

// 演算子 . は、構造体のメンバ変数でも、ポインタでアクセスすることができる
a1 := Person{"Tanaka", 26}	// 構造体Personのオブジェクトa1を確保して初期化
p1 := &a1			// 構造体a1へのポインタをp1に格納
fmt.Println(a1.name)		// メンバ変数には左記のようにアクセス
fmt.Println((*p1).name)		// ポインタpの中身(後続体)のメンバ変数には左記のようにアクセス
fmt.Println(p1.name)		// ただし、Go言語ではこれを、左記のようにも記述できる
```

# 領域確保 (new)

ポインタ周りの話なので、C#は割愛

new()句 を用いて領域を動的に確保し、その領域へのポインタを得ることができる。

確保した領域は参照されなくなった後にガベージコレクションにより自動的に開放される。

```go:Go
type Hoge struct {
	name string
}

hogeArr := []* Hoge{}
// 構造体のポインタを得る
hoge := new(Hoge)
hoge.name = "fuga" // (*hoge).name = "fuga" と同じ意味
```

# 参考資料

[A Tour of Go](https://go-tour-jp.appspot.com/list)

[とほほのGo言語入門 - とほほのWWW入門](https://www.tohoho-web.com/ex/golang.html)

たくさんのサイト