インドカレーファンクラブ

パソコン、カメラ

【Go】C#erが悩んだ構造体の初期化やコンストラクタ(微妙記事)

Goの構造体の初期化方法は色々あってよく迷うので整理した

C# (やさしい)

C#でクラスを初期化する時はこんな感じ

public class Character
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Character() {}

    public Character(string name)
        => Name = name;

    public Character(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

// コンストラクタで初期化
var c  = new Character("キャル", 14);
// デフォルトコンストラクタ + オブジェクト初期化子
var c2 = new Character() { Name = "キャル", Age = 14 };
var c3 = new Character   { Name = "キャル", Age = 14 }; // カッコを省略できる

// これでもいい
var c4 = new Character();
c4.Name = "キャル";
c4.Age = 14;

// これはやらないな...
var ex = new Character("キャル") { Age = 14 };

引数付きのコンストラクタによる初期化を見ることが一番多いかな
オブジェクト初期化子で初期化もできる

コンストラクタというか関数のオーバーロードができるから、
インスタンスを作らせる場合コンストラクタを複数設ける場合が多いと思う

Go(つらく厳しい)

Golangでは所謂クラスの役割を構造体(struct)が担う

なので今回は構造体の初期化の話になる
構造体っていうか構造体の型のポインタ型を初期化する話を考える(意味不明な日本語に遭遇、ここで額に手をあてる)
今回は*Character型のポインタ型の変数を作っていく(天を仰ぐ)

その初期化というかナントカカントカの手法が色々あってC#ほどシンプルではないのでまとめる

type Character struct {
    Name string
    Age int
}

func main() {
    // パターン1:compositeリテラルで初期化(&のやつ、とよく呼ばれる)
    // この一連の処理は c:= &Character... でも同じ
    var c *Character // *Character型のポインタ型 c を宣言
    c = &Character {
        Name: "キャル",
        Age: 14,
    }

    // 端折れる(あんま見ない気がする)
    c2 := &Character {"キャル", 14}

    // パターン2:newで初期化
    c3 := new(Character)
    c3.Name = "キャル"
    c3.Age = 14
}

structそのままだとこんな感じ

compositeリテラルの方はいいけど、newして後から色々と値を格納するのをService層とかでやると無駄に行というか呼び出しが増えて好きじゃない(他の処理と混ざるというか...)

で、よくあるのがここにNewメソッドみたいなのを別途足してそれを使う形
コンストラクタ関数と呼ぶらしい

type Character struct {
    Name string
    Age int
}

func NewCharacter(name string, age int) *Character {
    return &Character {
        Name: name,
        Age: age,
    }
}

func NewCharacterOnlyName(name string) *Character {
    return &Character {
        Name: name,
        // ここで0格納しなくてもintの初期値として0が入るけど書いた方がわかりやすい...
        Age: 0,
    }
}

func main() {
    // パターン3:コンストラクタ関数(Newなんとか)をつくる
    c   := NewCharacter("キャル", 14)
    c2 := NewCharacterOnlyName("キャル")
}

Golangにはオーバーロードがないのでコンストラクタ関数もいちいち名前を変えないといけない
すごく辛そう

でも通例上、このコンストラクタ関数を設ける感じみたい

参考:

bmf-tech - Golangのポインタ概要

Golangのコンストラクタとその利用方法についての考察 - Qiita