C#のプロパティの用法についての思案メモ

## 書き出し
とあるUnityのフレームワークの中で見つけたコードです。

```c#
int Counter;

void Update()
{
    if (Counter > 100)
    {
        // カウントが閾値を超えたときの処理
    }
}
```

なぜ`Counter`に値を代入した際にチェックしないで`Update`で毎フレームチェック[^update]しているのか……謎。
こういうときはプロパティを使ったほうがいいのでは……と思ったのですが、自分もプロパティってあまり使ったことがないなーと思ったので自分が思いつくなりの用法をメモします。
ざっと調べた感じ、プロパティの使い方についての記事は出てきても「どう使うか」を実際に解説した記事は見当たらなかったので。

プロパティとはなんぞや、という方は以下の記事を参照。
[プロパティ - C# によるプログラミング入門 | ++C++; // 未確認飛行 C](https://ufcpp.net/study/csharp/oo_property.html)


## 使い方メモ
### リスナの登録管理

```c#
private bool _isListening = false;

private bool IsListening
{
    get => _isListening;
    set
    {
        if (_isListening == value)
        {
            return;
        }

        _isListening = value;
        if (_isListening)
        {
            ExampleEvents.ExampleEvent += ExampleEventHandler;
        }
        else
        {
            ExampleEvents.ExampleEvent -= ExampleEventHandler;
        }
    }
}
```
`IsListening`に`true`か`false`を入れるだけでリスナの登録を管理できます。いろんなタイミングでリスナを登録/解除しているとカオスになるので、これを使うとかなり楽になります。探すときも`IsListening`を検索するだけでいいし。

### ログ出し
```c#
public enum BadStatus
{
    None,
    Poison,
    Paralyze,
}

private BadStatus _status = BadStatus.None;

public BadStatus Status
{
    get => _status;
    set
    {
        Debug.Log($"{_status} => {value}");
        _status = value;
    }
}
```

enumでStateを定義するのはよくあることだと思います。
以下のように変化させると

```c#
Status = BadStatus.Poison;
Status = BadStatus.Paralyze;
Status = BadStatus.None;
```
こんな感じでログが出ます。

```:ログ
None => Poison
Poison => Paralyze
Paralyze => None
```

ステート遷移の解析に役立ちます。重要なのは`None => None`など、同じステートへの変化でもログを省略しないこと。
うっとうしいですがその方が解析が楽です。

### 値のバリデート

```c#
public readonly int MaxHp = 200;
public int _hp;

public int HP
{
    get => _hp;
    set
    {
        if (value < 0)
        {
            value = 0;
        }
        else if (value > MaxHp)
        {
            value = MaxHp;
        }

        Debug.Log($"{_hp} => {value}");
        _hp = value;
    }
}
```

ゲームでよくあるHPの管理。
ダメージを受けたり回復されたり。

```c#
HP = 100;
HP -= 300;
HP += 10;
HP += 999999999;
```

どれだけ食らっても一定の範囲内に収まります。

```:ログ
0 => 100
100 => 0
0 => 10
10 => 200
```

### だんだん状態異常が効かなくなってくるやつ
```c#
public int ResistancePoison = 40;
public int _accumulatedPoison = 0;

public int AccumulatedPoison
{
    get => _accumulatedPoison;
    set
    {
        _accumulatedPoison = value;
        if (_accumulatedPoison > ResistancePoison)
        {
            Status = BadStatus.Poison;
            _accumulatedPoison = 0;
            ResistancePoison += 20;
        }
    }
}
```
毒の蓄積値が閾値以上になると値をリセットして毒状態になり、閾値を大きくします。

```c#
AccumulatedPoison += 50;
Debug.Log("Cure!");
Status = BadStatus.None;
AccumulatedPoison += 50;
Debug.Log("again...");
AccumulatedPoison += 50;
```

```:ログ
None => Poison
Cure!
Poison => None
again...
None => Poison
```

## 感想
**めっちゃバグりそう**
値のバリデートやログ出し程度に留めておかないと変な穴[^kotlin]にハマって死にそうですね。
あとクラスのメンバ変数をクラスの上に書く基本のスタイルだと、変数の部分にロジックが混ざってめっちゃ読みづらそうです。
最近はIDEがかしこいので変数とメソッドを取り混ぜて書いても[^real]なんとかなりますが、なんとかなるってだけでつらいです。すごくつらい。

なんかいい使い方あるよって方はなんか書いてわたしの蒙を啓いてください。

おしまい。

[^update]: 処理をUpdateのタイミングで行う必要もない...
[^kotlin]: kotlinだと`field`知らないとしぬ
[^real]: いやいるんですよそんなことする奴……いるんですよ！　しかも新しく書いたやつから下に書いてるだけで特に並びに関連性があるわけでもなく……