C#9.0 レコード理解度テスト 《コンストラクタ編》

# C#9.0 レコード理解度テスト 《コンストラクタ編》

C#9.0のレコードについて、
理解度を確認する問題を作ってみました。
今回のメインテーマはコンストラクタです。

初回なので、シンプルなコードを揃えています。
息抜きにぜひチャレンジしてください。

---
## 問題

次に12例の`record`定義を示します。
それぞれの例について、

* 「コンパイル可」「コンパイル不可」を答えてください。
  * 「コンパイル不可」と答えた場合は、その理由を答えてください。

> 環境は以下の通りです
> Visual Studio 2019
> C#9.0
> .NET 5.0
> null許容:有効化

---
### 基本編

#### 問1

```C#
record MyRecord(string Name, int Number)
{
}
```

#### 問2

```C#
record MyRecord()
{
    public string? Name { get; init; }
    public int Number { get; init; }
}
```

#### 問3

```C#
record MyRecord
{
    public string? Name { get; init; }
    public int Number { get; init; }
}
```

---
### 引数なしコンストラクタ編

#### 問4

```C#
record MyRecord(string Name, int Number)
{
    public MyRecord()
    {
        Name = "Unknown";
        Number = 0;
    }
}
```

#### 問5

```C#
record MyRecord()
{
    public string Name { get; init; }
    public int Number { get; init; }

    public MyRecord()
    {
        Name = "Unknown";
        Number = 0;
    }
}
```

#### 問6

```C#
record MyRecord
{
    public string Name { get; init; }
    public int Number { get; init; }

    public MyRecord()
    {
        Name = "Unknown";
        Number = 0;
    }
}
```

---
### 引数ありコンストラクタ編

#### 問7

```C#
record MyRecord(string Name, int Number)
{
    public MyRecord(string name)
    {
        Name = name;
    }
}
```

#### 問8

```C#
record MyRecord()
{
    public string Name { get; init; }
    public int Number { get; init; }

    public MyRecord(string name)
    {
        Name = name;
    }
}
```

#### 問9

```C#
record MyRecord
{
    public string Name { get; init; }
    public int Number { get; init; }

    public MyRecord(string name)
    {
        Name = name;
    }
}
```

---
### 本体書かない編

#### 問10

```C#
record MyRecord(string Name, int Number);
```

#### 問11

```C#
record MyRecord();
```

#### 問12

```C#
record MyRecord;
```

以上！

---
## 模範解答

今回のテーマはコンストラクタの違いでした。

考慮漏れがなかったかどうか確認していきましょう。

---
### 基本編

* 問1: コンパイル可
* 問2: コンパイル可
* 問3: コンパイル可

すべてコンパイルできます。
全く同じ定義というわけではなく、`new` するときに違いがあります。

```C#:問1
var r = new MyRecord("なまえ", 123);
```

```C#:問2,問3
var r = new MyRecord() { Name = "なまえ", Number = 123 };
```

```C#:問2,問3、その2
var r = new MyRecord();
```

問2、問3の例では、プロパティは初期化されないことがあります。

---
### 引数なしコンストラクタ編

* 問4: コンパイル不可
  * プライマリコンストラクタを呼び出していない。
* 問5: コンパイル不可
  * プライマリコンストラクタと定義が衝突している。
* 問6: コンパイル可

__位置指定パラメータ__ が指定されているレコードでは、 __プライマリコンストラクタ__ が生成されています。
コンストラクタは、基本的にプライマリコンストラクタを呼び出す必要があります。
また、問5では、本体に書いたコンストラクタはシグネチャが衝突してしまい、そもそも定義できません。

以下に、コンパイル可能な修正を示します。

_問4修正版_

```C#:thisを呼び出せばOK
public MyRecord() : this("Unknown", 0)
{
}
```

---
### 引数ありコンストラクタ編

* 問7: コンパイル不可
  * プライマリコンストラクタを呼び出していない。
* 問8: コンパイル不可
  * プライマリコンストラクタを呼び出していない。
* 問9: コンパイル可

こちらも、プライマリコンストラクタの呼び出しがテーマになります。

以下に、コンパイル可能な修正を示します。

_問7修正版_

```C#:thisを呼び出せばOK
public MyRecord(string name) : this(name, 0)
{
}
```

_問8修正版_

```C#:thisを呼び出せばOK
public MyRecord(string name) : this()
{
    Name = name;
}
```

問9はコンパイル可ですが、引数なしのコンストラクタが無いことに注意して下さい。
問3、問6と同じ方法での`new`はコンパイル不可になります。

```C#:問9ではNG
var r = new MyRecord() { Name = "なまえ", Number = 123 };
```

```C#:これならOK
var r = new MyRecord("なまえ") { Number = 123 };
```

問8修正版では、上記2つとも`new`できます。

---
### 本体書かない編

* 問10: コンパイル可
* 問11: コンパイル可
* 問12: コンパイル可

すべてコンパイル可能です。
問11、問12の例が役に立つかどうかはさておき、引数なしのコンストラクタはあるので`new`できます。

```C#:newはできる
var r = new MyRecord();
```

---
## おわりに

本当は『レコードには 位置指定パラメータ を使おう』というような記事を書こうと思っていました。
考えを整理するために色々試し始めたら、挙動の予想が出来ていない部分に気づき、これをまとめるための箸休め的な記事を書くことになりました。

少しでも皆さんの気づきの役に立っていれば幸いです。

> 次回はもう少し実のある記事を書きたい……