Unityで「シリアライズするフィールド」と「プロパティー」を簡潔に書きたくて、SourceGeneratorを作ってみた

※ この投稿で紹介するSourceGeneratorは、まだUnityじゃ使えません。将来的にこうできればいいなーって話

さてまずは、C#のプロパティーの話をします。
C#では、フィールドをpublicにするのは良くなくて、プロパティーを使ってフィールドにアクセスするのが良いとされています。


↓のコードは良くなくて

```csharp
public class Launcher {
    // publicなフィールドだからこれは良くない
    public float speed;
}
```

↓な感じでプロパティーにするのがGoodです。

```csharp
public class Launcher {
    float speed;

    // ちょっと古い書き方のプロパティー
    public float Speed {
        get { return speed; } 
        set { speed = value; }
    }
}
```

さて、↑みたいなコードはわざわざフィールドを作らなくても、↓のような自動実装プロパティーで実現ができます。

```csharp
public class Launcher {
    public float Speed { get; set; }
}
```

自動実装プロパティーを使うことで、C#のコードからは見えない次のようなフィールドとアクセッサが、"自動"で生成されます。

* `<Speed>k__BackingField`というフィールド
* `get_Speed`というメソッド
* `set_Speed`というメソッド

自動実装プロパティー、便利ですね！

---

さて次はUnityのシリアライズとフィールドとプロパティーの話。

UnityにおいてMonoBehaviourのサブクラスやScriptableObjectのサブクラスなどで、次のようなpublicなフィールドに設定した値や参照は、シーンやプレファブにシリアライズされることがあります。


```csharp
using UnityEngine;

public class Launcher : MonoBehaviour {
    // publicなフィールドだからこれは良くない
    public float speed;
}
```

ですが先にも説明したとおり、C#として↑のようなpublicなフィールドは良くなかったですね。
こういう場合は、SerializeField属性の出番です。SerializeField属性をつけたフィールドはpublicでなくても、シリアライズされます。

```csharp
using UnityEngine;

public class Launcher : MonoBehaviour {
    [SerializeField] float speed;
}
```


さて、このフィールドに外部からアクセスするために、プロパティーを付けてあげましょう。（とりあえずゲッタープロパティーだけ）

```csharp
using UnityEngine;

public class Launcher : MonoBehaviour {
    [SerializeField] float speed;
    public float Speed => speed;
}
```

うん、よくあるパターンです。 このUnityで「シリアライズするフィールド」と「プロパティー」というよくあるパターンをもっと簡潔にしたいな！というのがこの投稿の趣旨です。

---

先ほど説明した自動実装プロパティ、あれは使えないのでしょうか？

自動で実装される内部的なフィールド（バッキングフィールド）に、SerializeField属性をつけることができたら、実現できそうです。なんと、これ実現できます。

C# 7.3から自動実装プロパティの自動で実装される内部的なフィールド（バッキングフィールド）に属性を付与できるようになりました。

```csharp
using UnityEngine;

public class Launcher : MonoBehaviour {
    [field:SerializeField]
    public float Speed => speed;
}
```

うん、簡潔！めでたしめでたし！
とは、ならないいんです・・・

自動実装プロパティのフィールド名は、「`<Speed>k__BackingField`」という名前です。困ったことに、この名前でシリアライズされてしまいます。次の画像みたいな感じで。


![スクリーンショット 2020-12-04 1.06.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/138d27e6-850e-78af-0035-4c592e9ae8c4.png)
![スクリーンショット 2020-12-04 1.06.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/68cdadb8-c533-8b1c-0153-eaccf452560f.png)

うーん、困った。

---

さて、ここでC# 9.0から加わったSourceGeneratorの出番です。（まだUnityじゃ使えません。将来的にこうできればいいなーって話）

C# Source Generatorは、ビルド時にC#のソースコードを生成する仕組みです。

* メインのプロジェクトがビルドされる前にコード生成
* コード生成するために必要な入力値はコンパイル時に必要
* 出力結果は、プロジェクトの一部となる
* IDEにおいて、生成したコードの宣言にジャンプもできる
* ILではなくC#を生成するので、デバックがすごい楽
* 既存のソースコードを上書きしたりけしたりすることはできない


このC# Source Generatorを使うことで、プログラマティカルにコード生成をすることができます。C# Source Generatorを使うことで、ボイラープレートのコードは非常に簡単になります。

さて、自分はC# Source Generatorをつかって、「フィールドに付与すると、そのフィールドのゲッタープロパティーを生成してくれるSource Generator」を作ってみました！

ソースコードはこちら！ [RyotaMurohoshi/PropertyGenerator](https://github.com/RyotaMurohoshi/PropertyGenerator)

```csharp
using System;
using PropertyGenerator;

public partial class Product
{
    [GetterProperty(PropertyName = "Identifier")]
    private readonly int id;

    [GetterProperty] private readonly string name;

    public Product(string name, int id)
    {
        this.name = name;
        this.id = id;
    }
}
```

↑みたいな感じで、idやnameというフィールドにGetterPropertyという属性をつけると、↓のようなプロパティが生成されます。

```csharp
public partial class Product
{
    public int Identifier => this.id;
    public string Name => this.name;
}
```

このGetterPropertyを使って、次のようなボイラープレートなコードもC# Source Generator使ってすっきりさせてみましょう。


```csharp
using UnityEngine;

public class Launcher : MonoBehaviour {
    [SerializeField] float speed;
    public float Speed => speed;
}
```

↑が、↓こうなります！プロパティーがいらなくなりました！すっきり！

```csharp
using UnityEngine;

public class Launcher : MonoBehaviour {
    [SerializeField, GetterProperty] float speed;
}
```

あんまり嬉しくない？確かに1行だとそうですね。

これが↓みたいにたくさんあったらどうでしょう？

```csharp
using UnityEngine;

public class Monster : ScriptableObject {
    [SerializeField] int maxHp;
    public int MaxHp => maxHp;

    [SerializeField] int maxMp;
    public int MaxMp => maxHp;

    [SerializeField] int attack;
    public int Attack => attack;

    [SerializeField] int defense
    public int Defense => defense;

    [SerializeField] int speed
    public int Speed => speed;
}
```

これが↑、↓こうなる！すっきりしましたね！
ちなみに↑は、一部実装が間違っている箇所にきがつきましたか！↑はうっかりミスをしてます。↓ならこういううっかりミスも防げますね！

```csharp
using UnityEngine;

public class Monster : ScriptableObject {
    [SerializeField, GetterProperty] int maxHp;
    [SerializeField, GetterProperty] int maxMp;
    [SerializeField, GetterProperty] int attack;
    [SerializeField, GetterProperty] int defense
    [SerializeField, GetterProperty] int speed
}
```

---

Unityで「シリアライズするフィールド」と「プロパティー」ですが、残念ながら自動実装プロパティはつかえません。そこで、Unityで「シリアライズするフィールド」と「プロパティー」を簡潔に書きたくて、SourceGeneratorを作ってみました。フィールドにつけるとゲッタープロパティーを生成するという非常に簡潔なものです！

よかったらコードを見てみてください！ : [RyotaMurohoshi/PropertyGenerator](https://github.com/RyotaMurohoshi/PropertyGenerator)

これがUnityで使えるようになるのが楽しみです！