LoginSignup
4
4

More than 1 year has passed since last update.

【Unity】C#のコードをより短く分かり易くミスなく記述する

Last updated at Posted at 2021-12-17

今回の記事では,C#言語でコードをより綺麗に書く方法について記述しています。

もっと色々書こうと思ってたんですが,予想以上に何も思いつかなかったのでちょっと短めの記事になっています。

条件式での記述を省略

時々この書き方をしている人を見かけます。

bool test = true;

if (test == true)
{
    // 処理
}

確かにやっていることは正しいのですが,そもそも条件式の括弧内ってtruefalseの2種類しか入らないので,わざわざ書かなくてもいいです。

// 条件式がtrueだったらこれでよし
if (test)
{
    // 処理
}

// 条件式がfalseだったらこれ
if (!test)
{
    // 処理
}

foreachを使用する

ただ配列を回しているだけの場合はforeachを使用しましょう。

記述を省略(関数化)

クラス内で似たような処理を書いていると思った時は関数化しましょう。

// randomNum: 1 ~ 3までのランダムな数値が入力される。
// AAA(): 何かの処理をする関数

switch (randomNumber)
{
    case 1:
        AAA();
        Debug.Log("出た数値:" + 1);
        break;

     case 2:
        AAA();
        Debug.Log("出た数値:" + 2);
        break;

     case 3:
        AAA();
        Debug.Log("出た数値:" + 3);
        break;
}

上記のものは,先日実際に書かれているのを見た処理です。

この処理は,以下のように省略できます。

// これだけ
TestMethod(randomNumber);

private void TestMethod(int num)
{
    AAA();
    Debug.Log("出た数値:" + num);
}

本当にこれだけです。
たったこれだけの記述で数値が100まで出ようと1000まで出ようと作業量が増えることはありません。

上記のように場合によっては条件式すら要らなくなる場合があるので,もし「似たような処理を書いているな……」と思ったらコードを見直してみましょう。

記述を省略(継承)

例えば以下のようなクラスがあったとします。

Slime
public class Slime : Monobehaviour
{
    private int hp;
    private int mp;

    private int attack;
}
Dragon
public class Dragon : Monobehaviour
{
    private int hp;
    private int mp;

    private int attack;
}

敵(スライム・ドラゴン・ゴーレム)やプレイヤー(剣士・魔法使い・聖職者)など,多くの共通の機能を持つクラスは以下のように新しいクラスを作成し,それを継承することで解決します。

Enemy
public class Enemy : Monobehaviour
{
    protected int hp;
    protected int mp;

    protected int attack;
}

protectedというのはアクセス修飾子で,「宣言したクラス,または継承したクラス」でのみ値が使用できるようになります。
そしてこのEnemyというクラスを先ほどのSlimeDragonに継承してあげると,それぞれのクラスでステータスを実装する必要がなくなります。

Slime
public class Slime : Enemy
{
    private void Start()
    {
        // 値はそのまま使える
        hp = 10;
    }
}

$マークで文字連結をより簡単に

よくあるドルマーク($)のやつです。文字列補間といいます。

private int num = 0;
private string text = "hoge";

// パターン1
Debug.Log("num: " + num + ", text: " + text);
// パターン2
Debug.Log("num: {0}, text: {1}", num, text);
// パターン3: 文字列補間
Debug.Log($"num: {num}, text: {text}");
出力
num: 0, text: hoge

全て同じ出力結果となります。
最もシンプルな書き方で,最も分かり易いものが文字列補間ですね。

nameofで型名をそのまま出力

nameofという機能を使用すると,変数やメソッドの名前をそのまま出力できます。

private int num = 0;

private string TestMethod() 
{
    return "HogeHoge";
}

// 普通に書く
Debug.Log($"num: {num}");
Debug.Log($"TestMethod: {TestMethod()}");

// nameofを使用する
Debug.Log($"{nameof(num)}: {num}");
Debug.Log($"{nameof(TestMethod)}: {TestMethod()}");
出力
num: 0
TestMethod: HogeHoge

一見すると元のコードより幅が長くなり,「ただただめんどくさくなったのでは?」と思うかもしれませんが,この書き方をすることで誤字がまず起こらなくなります。
そしてコードの予測変換を使って入力することができるので,結果として上記のものよりも短い時間で入力できるようになります。

Unity使用者ならCoroutineやInvokeの引数をnameofで書くと実感が得られやすいと思います。

Switch式で分岐処理をより見やすく

switchは色々な書き方がありますが,今回紹介するSwitch式を使用すると処理がより見やすく簡潔になります。

// randomNum: 1 ~ 4までのランダムな数値が入力される。
private string fruit;

// 通常のSwitch文
switch (randomNumber)
{
    case 1: fruit = "Apple"; break;
    case 2: fruit = "Peach"; break;
    case 3: fruit = "Berry"; break;
    default: fruit = "Not Fruit"; break;
}

// Switch式を利用した場合
fruit = randomNumber switch
{
    1 => "Apple",
    2 => "Peach",
    3 => "Berry",
    _ => "Not Fruit"
}

値を直接代入するだけのようなシンプルな処理だった場合,Switch式を利用した方が簡単に記述することができます。ただ最後のdefaultにあたる部分の入力は必須です(ここでthrowでエラー投げることもできます)

switch式と通常のswitchを上手く使い分けて,簡単に開発をしてきましょう。

paramsの使用で引数を無限に取る

主にC#の出力系に用いられている機能で,これを使用すると引数を無限に入れることができます。

private void TestMethod(params string[] testStr)
{
    foreach (var str in testStr)
    {
        // 処理
    }
}

TestMethod("aaa", "bbb", "ccc", "ddd");

複数あるタグの判定等,様々な面で役に立ちます。

ジェネリックを使用し型に縛られない

コードを記述する際によくある「似た内容なのに型が違うから関数化できない」という問題を解決してくれる機能です。

もう少し具体例を挙げると,intやfloat,doubleなどの数値型のみを引数に取りたいといった場合や,クラス型のみを引数に取りたいといった場合に使えます。

これを書き始めると少し記事が長くなってしまうので,詳細を知りたい方は下記のサイトを参考にしてください。

高階関数の使用

中身の実装自体は殆ど同じ,でも呼び出す関数が少し違うといった場合に使用します。
関数化・ジェネリックの使用とのだいたいの違いは以下の形になるのではないでしょうか。

関数化: 決まった型(int, string, floatなど)の引数を取る
ジェネリック: 何かしらの型を引数に取るが,型が何かは決まっていない
高階関数: 型ではなく,関数そのものを引数にとる

といった感じです。
関数を引数に取る関数のことを高階関数と呼びます。

コードで理解したいという方は,下記のリンク先で非常に分かり易く解説してくれているので,そちらをご覧ください。

usingの記述量を大幅短縮

C#10.0から追加された機能にglobal using ディレクティブというものがあります。
これはプロジェクト全体でusingの記述が大幅に減るという素晴らし過ぎる機能なので,早く来て欲しいです。

SampleA
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SampleA : Monobehaviour
{
    // 処理
}
SampleB
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SampleB : Monobehaviour
{
    // 処理
}

現在は上記のように使用に応じてusingを宣言してあげる必要があるのですが,C#10.0からはプロジェクト内の何処かで以下のように宣言してあげるだけで,今後宣言した項目はusingする必要がなくなります。

global using System.Collections.Generic;
global using UnityEngine;
global using UnityEngine.UI;
public class SampleA : Monobehaviour
{
    // 処理
}
public class SampleB : Monobehaviour
{
    // 処理
}

namespaceの省略でネストを浅く

こちらもC#10.0の機能です。
今までnamespaceを宣言することでネストが1層は深くなっていたと思うのですが,C#10.0からはそれをしなくてもよくなりました。

現在のC#
namespace Creature
{
    public class Enemy : Monobehaviour
    {
        // 処理
    }
}
C#10.0
namespace Creature;

public class Enemy : Monobehaviour
{
    // 処理
}

デザインパターンの導入

デザインパターンとは,オブジェクト指向言語で「よく見かける問題」に対しての23個の解決方法です。
絶対に学ぶ必要のあるものではないんですが,エンジニア間での問題解決の共通認識としても扱われているので,時間に余裕が出来た時にでも学びましょう。

4
4
0

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
4
4