クラス

目次

キーワード

概要

クラスとはオブジェクトを作るための設計図のようなもので、 オブジェクト指向プログラミングの中心となるものです。

クラスとインスタンス

オブジェクト指向とは」 で述べたように、 操作の対象となるものをオブジェクトといいます。 オブジェクトを作る場合、まず設計図が必要になります。 内部がどういう構造になっているのか、外部からどのような操作をすることが出来るのかを決めてやるわけです。 このようなオブジェクトの設計図のことをクラス(class)といいます。 それに対し、設計図を元に作られたオブジェクトの実体のことをインスタンス(instance)といいます。

表1: クラスとインスタンスの比喩的例

クラスインスタンス
製品規格個々の製品
人間松井君、空知君、畑君、田辺さん・・・
実数全体 R実数値 x, y, ・・・
初等関数sin, cos, exp, log, ・・・

クラス定義

C#では以下のようにしてクラスを定義します。

class クラス名
{
  クラスの実装
}

クラスの実装にはメンバ変数の定義とメソッド(メンバ関数)の定義などをします。 メンバ変数とはクラスの内部で宣言される変数のことで、 メソッドの定義はクラスの内部で宣言される関数のことだと思ってもらって結構です。 以下に例を示します。

class Sample
{
  // メンバ変数の定義 ここから↓
  private int x;
  private int y;
  // メンバ変数の定義 ここまで↑

  // メソッドの定義 ここから↓
  public int GetX()
  {
    return x;
  }

  public int GetY()
  {
    return y;
  }

  public void Set(int a, int b)
  {
    x = a;
    y = b;
  }
  // メソッドの定義 ここまで↑
}

privatepublicといったキーワードについては「実装の隠蔽」で解説します。

メンバ変数によってオブジェクトの内部の実装を記述し、メソッドによって外部から行える操作を記述するわけです。 以下、具体的な例を挙げるために複素数を表すクラスを作ってみましょう。 まず、複素数に対する操作には何があるかを列挙してみましょう。

次に、複素数を実装する方法を考えて見ましょう。

いきなりすべてを実装するのは大変ですから、 まず、実部と虚部の取り出し・変更と、絶対値の取り出しを、 実部と虚部を記憶しておく方式で実装してみます。

class Complex
{
  public double re; // 実部を記憶しておく(外部からの読み出し・書き換えも可能)
  public double im; // 虚部を記憶しておく(外部からの読み出し・書き換えも可能)

  // 絶対値を取り出す
  public double Abs()
  {
    return Math.Sqrt(re*re + im*im);// Math.Sqrt は平方根を求める関数
  }
}

最初ということで、シンプルになるように実装しましたが、今後、徐々にちゃんとした形のものにしていきます。

クラスの利用

クラスを利用するためには、 インスタンスを作成しなければなりません。 そのためにまず、インスタンスを格納するための変数を定義します。 変数定義の仕方は以下のような構文になります。

クラス名 変数名;

次に、new キーワードでインスタンスを作成し、用意した変数に格納します。

ここで注意すべきことは、C# において、変数というのはただの入れ物であって、 変数を宣言しただけではインスタンスは作成されません。 (空っぽの入れ物だけができる。) 以下のように、new して始めてインスタンスが生成されます。

変数 = new クラス名();

そして、以下のように変数の後に「 . 」で区切ってメンバ名を書くことでメンバ変数やメンバ関数を利用できます。

変数名.メンバ名

例として先ほど作成した複素数クラスのインスタンスを生成し、利用してみましょう。

Complex z;         // インスタンスを格納するための変数を定義
z = new Complex(); // new を使ってインスタンスを生成

z.re = 3;             // 実部の値を変更
z.im = 4;             // 虚部の値を変更
double abs = z.Abs(); // z の絶対値を取得

Console.Write("abs = {0}\n", abs); // abs = 5 と表示される

また、組込み型や配列と同様に変数の宣言と同時にインスタンスを作成して初期化することも出来ます。

int n = 5;
string s = "abcde";
int[] array = new int[]{1, 2, 3, 4, 5};
Complex z = new Complex();

サンプル

using System;

/// <summary>
/// 複素数クラス
/// </summary>
class Complex
{
  public double re; // 実部
  public double im; // 虚部

  /// <summary>
  /// 絶対値を返す
  /// </summary>
  public double Abs()
  {
    return Math.Sqrt(re*re + im*im);
  }

  /// <summary>
  /// 文字列化する
  /// </summary>
  public override string ToString()
  {
    if(im >0)
      return re + "+i" + im;
    if(im < 0)
      return re + "-i" + (-im);
    return re.ToString();
  }
}// class Complex

//================================================
class ClassSample
{
  public static void Main()
  {
    Complex z = new Complex();

    z.re = GetDouble("実部を入力してください : ");
    z.im = GetDouble("虚部を入力してください : ");

    Console.Write("|{0}| = {1}\n", z, z.Abs());
  }

  // 「関数」のところで作った実数入力用関数
  static double GetDouble(string message)
  {
    double x;
    while(true)
    {
      try
      {
        // 入力を促すメッセージを表示して、値を入力してもらう
        Console.Write(message);
        x = double.Parse(Console.ReadLine());
      }
      catch(Exception)
      {
        // 不正な入力が行われた場合の処理
        Console.Write("error : 正しい値が入力されませんでした\n入力しなおしてください\n");
        continue;
      }
      break;
    }
    return x;
  }
}

クラスと構造体

ここまでの説明を見て、 クラス構造体の類似性に気付いた方もいるかと思います。 実際、メンバ変数やメソッドの定義は構造体でもできます。

クラスと構造体の違いを説明するためには、 継承や多態性などのオブジェクト指向の概念や、 値型と参照型というプログラミングの概念の理解が必要になります。 これらの概念の詳細は、 「継承」 」、 「多態性」 、 「値型と参照型」 などで説明することにして、 ここでは簡単に概要だけを表にまとめます。

表2: クラスと構造体

クラス構造体
型の分類参照型値型
継承できるできない
多態性使える使えない

クラスの分割定義

Ver. 2.0

C# 1.1 以前は、クラスの定義は全て1つのファイルに収める必要がありました。 まあ、1つのクラスの中身が、複数のファイルに散在するとソースファイルの可読性が低くなるので、 通常は、むしろ、クラス定義を複数のファイルに分割できない方がいいのですが、 時折、クラス定義を分割したい場面があります。

例えば、Visual Studio などの統合開発環境を利用していると分かると思いますが、 ソースファイルにも、 統合開発環境が自動的に生成してくれる部分と、 自分の手で書く部分とがあります。 この、自動生成部分と手書きの部分は、別ファイルに分かれている方が (間違えて自動生成部分を壊してしまう危険性が減って)都合が良かったりします。

そこで、C# 2.0 では、クラス定義時に partial というキーワードを付けることで、 クラス定義を複数に分割することができるようになりました。 例えば、(少々無理やりな例ですが)上述の Complex クラスを2つに分割してみましょう。

partial class Complex
{
  public double re;
  public double im;
}

partial class Complex
{
  public double Abs()
  {
    return Math.Sqrt(re * re + im * im);
  }
}

この2分割された Complex クラスは、別々のファイル中にあっても構いません。 ただし、どの分割された部分にも、必ず partial というキーワードをつける必要があります。

class Complex // partial の付いていないクラスに、
{
  public double re;
  public double im;
}

partial class Complex // 別途、partial を追加することはできない。エラーになる。
{
  public double Abs()
  {
    return Math.Sqrt(re * re + im * im);
  }
}