【C#】構造体の使い方(クラスとの違い、初期化や配列の使用も解説)

構造体って使ってますか?

構造体では異なるデータ型をまとめて扱うことができます。また、C#では処理することもできます。

この記事では、構造体について

  • 構造体とは
  • 構造体とクラス
  • 構造体の定義、初期化
  • 構造体のフィールドに配列を用いる
  • 構造体を要素とする配列

など基本的な内容から、応用的な使い方についても解説していきます。

今回は構造体について、使い方をわかりやすく解説します。

目次

構造体とは

構造体とは、ある対象に関連する項目をまとめて1つのかたまりにしたものです。

関連する項目はフィールド(メンバ変数)と呼ばれ、変数や文字列などをフィールドとすることができます。C#ではこのフィールドを使って処理を定義することが可能です。

同じように項目をまとめて1つのかたまりにしたものに配列があります。配列の場合は同じ型のモノしか1つのかたまりにできません。

構造体はint型や文字列など型の違うモノでも1つのかたまりにできます。また、C#では処理を定義することもできますので、クラスとよく似ています。

構造体とクラスとの共通点や違いについてはこの後詳しく解説します。

構造体とクラス

C#では構造体とクラスがとてもよく似ています。

そこで、共通点は何か違いが何かをまとめてみましょう。

共通点

クラスとの共通点について以下のようにまとめてみました。

    • インスタンス化して使用

使用する際にはインスタンス化します。

    • メソッドの定義

フィールドだけでなく、メソッドを定義して処理を行うことができます。

    • 引数ありコンストラクタの定義

インスタンス化してフィールドの値を初期化する場合などに使用します。

    • インターフェースの実装(複数実装も可)

インターフェースを実装できます。複数のインターフェースを実装することも可能です。

違い

構造体とクラスとの違いについて表にまとめてみました。

大きな違いはインスタンス化したオブジェクトの型です。

構造体は値型ですが、クラスは参照型です。

構造体クラス
オブジェクトの型値型参照型
クラスの継承不可
(System.ValuTypeのみ継承
継承元にはなれない)
可能
フィールド初期化子の使用不可可能

デフォルトコンストラクタ(引数なし)の定義
不可可能
デストラクタの定義不可可能

表のとおり、構造体はクラスに比べて制限が多いです。

使い分け

クラスに比べて制限の多い構造体ですが、軽量のオブジェクトを表すのに適していると言われています。

参照型の機能が必要ない場合は、クラスとして実装するよりは構造体として定義した方が効率的に処理されるようです。クラスと構造体を100万個ずつインスタンス化した場合、クラスが約100msに対して構造体では30msと処理時間が短い場合もあるようです。

例えば下記のような3次元の点座標を大量に扱うCGなどで使用する場合は構造体を用いた方が効率的かもしれません。

public struct Grid
{
  public int x;
  public int y;
  public int z;
}

構造体の定義

それでは構造体を使う上で定義の仕方からみていきましょう。

基本的な定義

構造体はキーワードstructを使って以下のように定義します。

[修飾子] struct 構造体名
{
  フィールド(メンバ変数);
}

例えば、3次元の点座標を扱う場合は以下のようになります。

public struct Grid
{
  public int x;
  public int y;
  public int z;
}

コンストラクタの定義

構造体では引数ありのコンストラクタを定義することができます。

例えば以下のようになります。

public struct Grid
{
  public int x;
  public int y;
  public int z;
  
  public Grid(int x, int y, int z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
}

メソッドの定義

構造体でメソッドを定義することもできます。

例えば以下のようになります。

public struct Circle
{
  public double r;
  
  public Circle(double rad) {
    r = rad;
  }
  
  public double CalcCircum(double r) {
    return 3.14 * 2 * r;
  }
  
  public double CalcArea(double r) {
    return 3.14 * r * r;
  }
}

構造体を定義する際の注意

構造体のフィールドは初期化子を使用することはできません。

また、引数なしのコンストラクタを定義することもできません。

public struct Grid
{
  public int x;
  public int y;
  public int z;
  
  /* 初期化子の使用はコンパイルエラー
  public int x = 0;
  public int y = 0;
  public int z = 0;
  */
  
  public Grid(int x, int y, int z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
  
  /* 引数なしのコンストラクタの定義はコンパイルエラー
  public Grid() {
    this.x = 0;
    this.y = 0;
    this.z = 0;
  }
  */
}

構造体の初期化

構造体をインスタンス化して初期化する方法はいくつかあります。

クラスと同様にnew演算子を使う方法もありますし、new演算子を使わない方法もあります。

サンプルコードで確認しましょう。

using System;

namespace Sample
{
  public struct Circle
  {
    public double r;
    
    public double CalcCircum(double r) {
      return 3.14 * 2 * r;
    }
    
    public double CalcArea(double r) {
      return 3.14 * r * r;
    }
  }
  
  class Sample
  {
    static void Main()
    {
      // 方法1.new演算子を使う方法
      Circle c1 = new Circle();
      c1.r = 10.0;
      Console.WriteLine("半径{0}の円周は{1}、面積は{2}", c1.r, c1.CalcCircum(c1.r), c1.CalcArea(c1.r));
      
      // 方法2.new演算子を使わない方法
      Circle c2;
      c2.r = 20.0;
      Console.WriteLine("半径{0}の円周は{1}、面積は{2}", c2.r, c2.CalcCircum(c2.r), c2.CalcArea(c2.r));
      
      // 方法3.インスタンス化と同時に初期化
      Circle c3 = new Circle() {r = 30.0};
      Console.WriteLine("半径{0}の円周は{1}、面積は{2}", c3.r, c3.CalcCircum(c3.r), c3.CalcArea(c3.r));
      
      Console.ReadKey();
    }
  }
}

実行結果:

半径10の円周は62.8、面積は314
半径20の円周は125.6、面積は1256
半径30の円周は188.4、面積は282

構造体のフィールドに配列を用いる

構造体のフィールドには、int型やdouble型、string型を指定できますし、配列を指定することもできます。

フィールドに配列を指定したサンプルコードを見てみましょう。

using System;

namespace Sample
{
  public struct Country
  {
    public string capital;
    public string[] cities;
  }
  
  class Sample
  {
    static void Main()
    {
      Country japan = new Country() {
        capital = "Tokyo", 
        cities = new string[]{"Osaka", "Nagoya"}
      };
      Console.WriteLine(
        "日本の首都は{0}、代表的な都市は{1}", 
        japan.capital, string.Join(", ", japan.cities)
      );
      
      Country usa = new Country() {
        capital = "Washington, D.C.", 
        cities = new string[]{"NY", "LA"}
      };
      Console.WriteLine(
        "アメリカの首都は{0}、代表的な都市は{1}", 
        usa.capital, string.Join(", ", usa.cities)
      );
      
      Console.ReadKey();
    }
  }
}

実行結果:

日本の首都はTokyo、代表的な都市はOsaka, Nagoya
アメリカの首都はWashington, D.C.、代表的な都市はNY, LA

構造体を要素とする配列

配列の要素に構造体を指定することも可能です。

サンプルコードで確認しましょう。

using System;

namespace Sample
{
  public struct Grid
  {
    public double x;
    public double y;
  }
  
  class Sample
  {
    static void Main()
    {
      Grid[] grids = new Grid[5];
      
      for(int i = 0; i < 5; i++) {
        grids[i] = new Grid() {x = i * 1.0, y = 2 * i * 1.0};
      }
      
      foreach(var grid in grids) {
        Console.WriteLine("x = {0}, y = {1}", grid.x, grid.y);
      }
      
      Console.ReadKey();
    }
  }
}

実行結果:

x = 0, y = 0
x = 1, y = 2
x = 2, y = 4
x = 3, y = 6
x = 4, y = 8

まとめ

ここでは構造体について説明しました。

構造体は、ある対象に関連する項目をまとめて1つのかたまりにしたものです。クラスとよく似ていますが、軽量のオブジェクトを表すのに適しています。

使いこなすことができるように、この記事を何度も参考にして下さいね!

この記事を書いた人

熊本在住のフリープログラマ兼ライターです。C/C++/C#、Java、Python、HTML/CSS、PHPを使ってプログラミングをしています。専門は画像処理で最近は機械学習、ディープラーニングにはまっています。幅広くやってきた経験を活かしてポイントをわかりやすくお伝えしようと思います。
お問合せはこちらでも受け付けています。
info@sss-lab.com

目次