[C#] 56. 値の初期化及び基本データ値(default)を設定する方法、そして原始データのnull処理、?と??の使い方


Study / C#    作成日付 : 2021/10/21 18:54:41   修正日付 : 2021/10/21 18:55:58

こんにちは。明月です。


この投稿はC#の値の初期化及び基本データ値(default)を設定する方法、そして原始データのnull処理、?と??の使い方に関する説明です。


我々がプログラムを作成すると変数の初期値を設定する場合があります。

例えばintタイプのメンバー変数を作成すると初期データを-1にするか? 0にするかの悩みがある場合があります。もちろん、-1に設定するか、0に設定するかに関しては仕様により設定しなければならないですが、int a = 0;式の設定よりもっとプログラムらしいな設定がないかな?

using System;
namespace Example
{
  // 例クラス
  class Node
  {
    // メンバー変数のdataにintタイプの基本データ
    private int data = default(int);
    // プロパティ
    public int Data
    {
      get
      {
        // メンバー変数のdataをリターン
        return this.data;
      }
    }
  }
  // Programクラス
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // インスタンス生成
      var node = new Node();
      // nodeのメンバー変数のdataをコンソールに出力
      Console.WriteLine(node.Data);

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例をみればintタイプのdataメンバー変数にdefaultキーワードを使ってintの初期値を設定しました。コンソールに出力すると0の値が出力されますね。

つまり、defaultは該当なデータタイプの初期データの値をリターンするキーワードです。

原始データの場合は初期intの場合0を、floatの場合は0.0を設定します。


それならここでメンバー変数の値にdefault(int)を外して出力するとどの値がリターンされるかな?

using System;
namespace Example
{
  // 例クラス
  class Node
  {
    // メンバー変数
    private int data;
    // プロパティ
    public int Data
    {
      get
      {
        // メンバー変数のdataをリターン
        return this.data;
      }
    }
  }
  // Programクラス
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // インスタンス生成
      var node = new Node();
      // nodeのメンバー変数のdataをコンソールに出力
      Console.WriteLine(node.Data);

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


同じの0の値があります。そうれならdefaultを設定する必要がないかな?正解は設定する必要がありません。

基本的にメンバー変数は基本初期値が設定されます。つまり、default(int)を作成しなくてもdefault(int)値で設定することです。でも、仕様によりintの値にnullを許して設定したい場合があります。

つまり、基本値が0ではなくnullをします。

using System;
namespace Example
{
  // 例クラス
  class Node
  {
    // 原始データタイプに?を入れたらnullを許す。
    private int? data;
    // プロパティ
    public int? Data
    {
      get
      {
        // メンバー変数dataをリターン
        return this.data;
      }
    }
  }
  // Programクラス
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // インスタンス生成
      var node = new Node();
      // nodeのメンバー変数のdataがnullなら
      if(node.Data == null)
      {
        // コンソールに出力
        Console.WriteLine("node.Data is null!");
      }
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


データタイプに?を入れたら原始データにnullを許します。そしてdefault(int?)はnullです。つまり、、基本初期値がnullになります。

原始データではないクラスの場合はどうでしょう?

using System;
namespace Example
{
  // 例クラス
  class Node
  {
    // stringは原始データではなく、クラスだ。
    private string data;
    // プロパティ
    public string Data
    {
      get
      {
        // メンバー変数のdataをリターン
        return this.data;
      }
    }
  }
  // Programクラス
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // インスタンス生成
      var node = new Node();
      // nodeのメンバー変数のdataがnullなら
      if(node.Data == null)
      {
        // コンソールに出力
        Console.WriteLine("node.Data is null!");
      }
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


classは基本的にnullを許すため、?を入れなくてもdefault(string)はnullの値です。


そうすると原始データとclassの差異が何があるので、この差異があるかな?基本的にC#には原始データを構造体(struct)で認識しています。


つまり、構造体はnullを許せません。

私がstructで構造体を生成すると使い方は基本的にclassと似てますがnullを許せません。


構造体でnullを許すためには?を使わなければならないです。


ここでまた知りたいことがメンバー変数はdefaultを使わなくてもクラスの場合はnullになるし構造体(Struct)は基本値が設定することが分かりますが、defaultというキーワードは意味がないじゃないかと思います。

でも、ジェネリック(Generic)にはクラス内部でデータタイプが決めてないですね。

link - [C#] 32. ジェネリックタイプ(Generic Type)を使い方

ジェネリックというのはクラス内部でデータタイプを設定することではなく、インスタンスを生成する時に設定することで説明しました。

using System;

namespace Example
{
  // ジェネリッククラス
  class Node<T>
  {
    // 基本default値をリターンする関数
    public T GetDefault()
    {
      return default(T);
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // Nodeクラスのジェネリックタイプをintに設定
      var node1 = new Node<int>();
      // 値がnullではなければ
      if (node1.GetDefault() != null)
      {
        // コンソールに出力
        Console.WriteLine(node1.GetDefault());
      }
      else
      {
        // コンソールに出力
        Console.WriteLine("node1 is null!");
      }
      // Nodeクラスのジェネリックタイプをstringに設定
      var node2 = new Node<string>();
      // 値がnullではなければ
      if (node2.GetDefault() != null)
      {
        // コンソールに出力
        Console.WriteLine(node2.GetDefault());
      }
      else
      {
        // コンソールに出力
        Console.WriteLine("node2 is null");
      }
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


つまり、ジェネリックではクラスに設定されるか構造体に設定されるかをインスタンスを生成するところで設定するので、内部のジェネリックタイプ(Generic)の値はdefaultで値を設定しなければならないです。


上の構造体をクラスみたいにnullを許すためにはデータタイプに?を付ければできると説明しました。

追加的の?の使い方がありますが、三元演算子のnullチェックです。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // stringタイプにnullを入れる。
      string node = null;
      // nodeの値で空白を取り除く。
      Console.WriteLine(node.Trim());
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


同然に上みたいに作成すると100%エラーが発生します。なぜなら、nodeの変数はnullですが、インスタンスの関数を実行しようと呼び出しました。

link - [C#] 11. インスタンスう生成(new)とメモリ割り当て(StackメモリとHeapメモリ)そしてヌル(null)


なので普通はif(node != null) { }のnullをチェックする分岐式を実装します。

でも、?を使ったら上のif(node != null) { }の意味の分岐式が含めることになります。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // stringタイプにnullを入れる。
      string node = null;
      // nodeの値がnullではなければ空白を取り除く。
      Console.WriteLine(node?.Trim());
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


結果はnode?.Trim()をすると空白がないstringではなく、続けてnullがリターンします。Console.WriteLine関数はnull値が入ると空白がない改行が出力されます。

?の意味はnullではなければ実行するという意味は分かりますが、nullなら別の処理をしたいです。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // stringタイプにnullを入れる。
      string node = null;
      // nodeの値にnullではなければ空白を取り除く。後でnullなら別のString値を出力する。
      Console.WriteLine(node?.Trim()??"This is null!");
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上でnode?.Trim()の結果はnodeがnullなので、nullになります。??はnode?.Trim()がnullなので、別の値で置換します。つまり、nodeに値があれば値を出力、なければ別の置換される値が出力されることです。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // stringタイプに文字列を入れる。
      string node = "Hello world      ";
      // nodeの値がnullではなければ空白を取り除く。
      Console.WriteLine(node?.Trim()??"This is null!");
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


ここまでC#の値の初期化及び基本データ値(default)を設定する方法、そして原始データのnull処理、?と??の使い方に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿