2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GoエンジニアがC#を学ぶ<型、変数、定数>

2
Last updated at Posted at 2026-03-05

まえがき

最近までGoを書いていた私がC#に触れ始めました。今回は「型・変数・定数」まわりについて理解したことを、Goとの比較を踏まえながら記事にしていこうと思います!
また比較といっていますが両言語は思想が異なるため、優劣をつける意図はありません。用途と前提が違うだけという視点でフラットに見ていきます。

値型と参照型

C# は静的型付けで、型は大きく 値型参照型 に分かれます。

  • 値型:(例: struct, int, bool, DateTimeなど)
    • 値そのもの”を持つイメージ
  • 参照型:(例:class, string, object, 配列など)
    • 値の“参照(アドレス)”を持つイメージ

Goとの比較

Go も静的型付けですが、値型が基本になります。もし関数の引数に変数の参照を渡したいと思った場合は、ポインターを使用します。
後に解説するスライスなどは参照型みたいに思われがちですが、こちらも内部的にはポインターが使われているため値型といえます。

ポインターの例
func main() {
    var x int = 10
    // &xにすることで、変数xの参照を引数として渡している
    increment(&x)
    // 参照元の値が変わっている
    fmt.Println(x) // 11
}

// intのポインターを引数として受け取る関数
func increment(p *int) {
    *p++
}

比較ポイント

  1. C#では大きく「値型」「参照型」 に分かれる。
  2. Goは「値型」が基本だが、「参照型」にするならポインターと組み合わせる。

クラス/構造体/レコード

C#のクラス(class)/構造体(struct)/レコード(record)は、用途に応じて使い分けるデータ型です。
組み合わせによっても機能が変わったりもするので以下で整理して記載します。

クラス(class)

  • : 参照型
  • 継承: できる
  • 等価性: メモリ内の同じオブジェクトを参照していれば等価
  • 用途: オブジェクト指向(振る舞い)の定義

構造体(struct)

  • : 値型
  • 継承: できない
  • 等価性: 型が同じで同じメンバ値が格納されていれば等価
  • 用途: 小さく軽いデータ(座標、識別子、日時など)と相性が良い

レコード(record class / record struct)

  • レコード型には、record classrecord structがある。(recordだけを使う書き方もあるがrecord classと同じ意味)
  • :
    • record class は参照型
    • record struct は値型
  • 継承:
    • record class はできる
    • record struct はできない
  • 等価性: 型が同じで同じメンバ値が格納されていれば等価
  • 用途:
    • record class は、DTO/設定/レスポンス等データ中心
    • record struct は、値として扱うDTO/小さめデータ(住所、バリューオブジェクトなど)
クラス/構造体/レコード定義の例
// クラス
public class Person
{
    public required string LastName { get; set; }
    public required string FirstName { get; set; }
}

// レコード
public record struct Point
{
    public double X { get; init; }
    public double Y { get; init; }
    public double Z { get; init; }
}

// 構造体
public struct Coords
{
    public double X { get; }
    public double Y { get; }
}

Goとの比較

Goには構造体(struct)というものがあります。

Go の struct値型として振る舞うのが基本となっておりますが、ポインターを使用(*struct)することで参照型のように振る舞うことも可能です。
またGoには継承という概念がなく、構造体に型を埋め込む(embedding) ことで埋め込んだ型の実装を借りることができます。

構造体と埋め込みの例
// 構造体(person)
type person struct {
	Name string
}

// 構造体(person)はgreet関数を持つ
func (p person) greet() string {
	return fmt.Sprintf("Hello, %s", p.Name)
}

// 構造体(citizen)
type citizen struct {
	person // 構造体(person)を埋め込み
	Country string
}

func main() {
	c := citizen{
		Country: "japan",
	}
    // 構造体(person)が埋め込まれているためNameのフィールドを持つ
	c.Name = "hoge"
    // 構造体(person)が埋め込まれているためgreet関数を持つ
	greeting := c.greet()

	fmt.Println(greeting)
}

比較ポイント

  1. C# は 参照型/値型 + クラス(class)/構造体(struct)/レコード(record)定義 で「表現力」が高い
  2. Go は ポインター + struct + 埋め込み(embedding) の「組み合わせ」が強い。

配列とリスト

C#には、固定長の配列T[N])と可変長のリストList<T>)があります。

配列の例
int[] array = new int[5];
array[0] = 1;
array[1] = 2;
リストの例
List<int> list = new List<>();
list.Add(1); // 追加
list.Add(2);

Goとの比較

Goには、固定長の配列[N]T)と可変長のスライス[]T)があります。

配列の例
var arr [4]int
arr[0] = 1
arr[1] = 2
スライスの例
s := make([]int)
// または
s := []int{}

s = append(s, 1) // 追加
s = append(s, 2)

比較ポイント

  1. C# は固定長の配列T[N])と可変長のリストList<T>)がある
  2. Go は固定長の配列[N]T)と可変長のスライス[]T)がある

代表的な型の比較(全体)

これまでの内容を踏まえて、プリミティブ型を含む型をまとめると以下になります。

目的 C# Go メモ
整数(符号付き) sbyte / short / int / long int8 / int16 / int32 / int64 C#はサイズが明確、Goにはintもあるが環境依存(32/64)
整数(符号なし) byte / ushort / uint / ulong uint8(byte) / uint16 / uint32 / uint64 C#はサイズが明確、Goにはuintもあるが環境依存(32/64)
浮動小数 float / double float32 / float64
高精度小数 decimal なし ※float64は丸め誤差を含んでしまう
真偽 bool bool
文字 char rune
文字列 string string C#のstringは参照型、Goは値型(ポインターで参照型)
時刻 DateTime / DateTimeOffset time.Time
配列/可変長 T[N] / List<T> [N]T / []T(スライス)
構造体/オブジェクト class/ struct/ record struct C#は表現が多い、Goはポインターなどとの組み合わせで表現

変数

変数定義

C# は「型を書く」か「型推論(var)」が基本です。

  • 明示的に型を書く
  • var で型推論
  • ローカル変数は 代入してから使用(未初期化のまま使えない)
変数定義の例
// 明示的に型を指定
int count = 10;
string name = "gopher";

// 型推論
var total = 123;          // int
var message = "hello";    // string

// 宣言だけして後で代入(初期化していないとコンパイルエラーになる)
int x;
x = 5;

Goとの比較

Go は var:= で書くのが基本になります。
:=varの省略形で、使用頻度はかなり高いです。

C#との大きな違いとしては、Goにはゼロ値という概念があり、変数は代入しなくても初期化されるという点です。
例えば、int型の場合は0で、string型の場合は""で初期化されます。

ゼロ値についてもっと詳しく(サンプルコードあり)

Go はすべての型に ゼロ値があります。var で宣言するとゼロ値で初期化されます。

  • 数値: 0
  • bool: false
  • string: ""(空文字)
  • ポインタ / slice / map / chan / func / interface: nil
  • struct: 各フィールドがゼロ値(いわゆる“ゼロ値struct”)
ゼロ値の例

type User struct {
    Name string
    Age  int
}

func main() {
    var n int
    var ok bool
    var s string
    var p *User
    var sl []int
    var m map[string]int
    var u User

    fmt.Println(n)        // 0
    fmt.Println(ok)       // false
    fmt.Println(s)        // ""
    fmt.Println(p == nil) // true
    fmt.Println(sl == nil)// true
    fmt.Println(m == nil) // true
    fmt.Printf("%+v\n", u) // {Name: Age:0}
}
変数定義の例
// 明示的に型を指定
var count int = 10

// 型推論
var name = "gopher"

// 短縮宣言(よく使う)
total := 123
message := "hello"

// 宣言だけしても代入せずに使える(0(ゼロ値)が初期値となっている)
var x int

比較ポイント

  1. どちらにも型推論がある(C#:var / Go::=var
  2. C#は代入していない変数は使えずコンパイル時にエラーになる、Go は変数を宣言時にゼロ値で初期化される

定数

constreadonly

C# は「定数」を宣言する方法が2種類あるのが特徴です。

  • const
    • コンパイル時に値が決定
    • 整数型や文字列型 など、扱える型に制限がある
  • readonly
    • 実行時に値が決定する“実質定数”
    • フィールドに付ける(static readonlyもある)
    • 扱える型に制限はない
定数の例
public class Config
{
    // コンパイル時に決定
    public const int MaxRetry = 3;

    // 実行時に決定
    public static readonly DateTime StartedAt = DateTime.UtcNow;

    // インスタンス生成時に決定
    public readonly string Name;

    public Config(string name)
    {
        Name = name; // コンストラクタ内で(インスタンス生成時に)代入
    }
}

Goとの比較

Go の定数にするかの判断はシンプルで、定数にできるならconst、無理なら var で定義します。

定数の例
// 定数
const MaxRetry = 3

// 実行時に決まるものはconstにできないのでvar
var StartedAt = time.Now().UTC()

比較ポイント

  1. C#は、値が決定するタイミングでconstreadonlyで使い分ける
  2. Goは、定数はconstそれ以外はvarでシンプルに使い分ける

まとめ

  • C#の型は「値型/参照型」がある。Goは「値型」が基本
  • C#はclass/struct/recordなどで型を表現しやすい。Goはstructやポインターの組み合わせで表現。
  • 変数定義は、C#はvar、Goはvar / :=で定義できる。どちらも型推論がある。Goの変数にはゼロ値があり変数の宣言をするだけで初期値が設定される。
  • 定数定義は、C#は値が決定するタイミングでconstreadonlyで使い分ける、Goは基本constでそれ以外はvar

今回は「型・変数・定数」まわりを記事にしてみました。
Goは比較的シンプルなのに対して、C#はどう表現するかによって扱いが変わってくるので今後のプロジェクトでの注意しながら開発していこうと思います!

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?