クラスの基礎

ごくシンプルなクラス

まずはごくシンプルなクラスの定義してみます。


//SimpleClassという名前のクラスを定義
class SimpleClass
{
    public void TestMethod()
    {
        Console.WriteLine("TestMethod実行");
    }
}

static void Main(string[] args)
{
    //SimpleClassのインスタンス生成
    SimpleClass sc = new SimpleClass();

    //SimpleClassのTestMethod実行
    sc.TestMethod();

    Console.ReadLine();
}        
TestMethod実行

クラスはclassというキーワードで作成します。
クラスの名称は自由です。
(変数名やメソッド名と同じ制限はあります)

定義したSimpleClassを実際に使うには、newキーワードでSimpleClassの変数を作成します。
この変数をインスタンスといいます。

インスタンスとは「実体」という意味です。
クラスはデータの「設計図」であり、設計図を元にして作られる「実体」がインスタンスというわけです。
設計図を元にして、実体であるインスタンスはいくらでも作ることができ、それぞれのインスタンスで独立した値を持つことができます。

インスタンスscからはSimpleClass内で定義したメソッドを呼び出すことができます。
メソッドの呼び出しはインスタンス名に続いて「.」(ドット)を記述し、目的のメソッド名を記述します。
これは今までに幾度となく登場してきたので難しくはないでしょう。

クラス内に定義される変数(フィールド)や関数(メソッド)などをメンバー(Member)と言います。

メソッド

クラスは内部にメソッド(Method)を持つことができます。
メソッドとは要するに関数のことです。

メソッドの定義方法は今までやってきた自作メソッドの定義と基本的に同じです。
ただし、今まで先頭に付けていたstaticは記述しません。

その代わりというわけではありませんが、今回は先頭にpublicというキーワードを付けます。
これの意味は後述します。
staticの意味はstaticの項で説明します。

フィールド

クラスはメソッドだけではなく、変数も持つことができます。
クラス内の変数をC#ではフィールド(Field)といいます。

C++では、クラス内変数をメンバ変数といいます。
クラス内関数をメンバ関数といいます。


class SimpleClass
{
    //フィールド
    int fieldInt;
    string fieldStr;

    public void TestMethod()
    {
        //これはただのローカル変数
        int num = 0;

        Console.WriteLine("TestMethod実行");
        Console.WriteLine(fieldInt);
        Console.WriteLine(fieldStr);
    }
}

static void Main(string[] args)
{
    //SimpleClassのインスタンス生成
    SimpleClass sc = new SimpleClass();
    
    //SimpleClassのTestMethod実行
    sc.TestMethod();

    Console.ReadLine();
}
TestMethod実行
0

ローカル変数は初期化してからでないと使用できませんが、フィールドは初期化しない場合は自動的に規定値で初期化されます。
なので、上記コードのようにフィールドを初期化せずに使用してもエラーになりません。

フィールドの初期化

フィールドは宣言と同時に初期化することもできます。


class SimpleClass
{
    int fieldInt = 10;
    string fieldStr = "test";
}

インスタンス

クラスはデータの設計図で、定義しただけでは実際のデータ(実体)は持ちません。
設計図に基づいて作られる実際のデータがインスタンスです。

インスタンスはいくつでも作ることができ、それぞれで独立した値を内部に持つことができます。


class SimpleClass
{
    public int FieldInt;
    public string FieldStr;
}

static void Main(string[] args)
{
    //インスタンスを生成
    SimpleClass sc1 = new SimpleClass();
    SimpleClass sc2 = new SimpleClass();

    sc1.FieldInt = 10;
    sc1.FieldStr = "aaa";

    sc2.FieldInt = 20;
    sc2.FieldStr = "bbb";

    Console.WriteLine("{0}, {1}", sc1.FieldInt, sc1.FieldStr);
    Console.WriteLine("{0}, {1}", sc2.FieldInt, sc2.FieldStr);

    Console.ReadLine();
}
10, aaa
20, bbb

アクセス修飾子

TestMethodの先頭につけられているpublicアクセス修飾子というものです。
アクセス修飾子は、外部からのアクセスを許可するかしないか(アクセスレベル)を設定します。
アクセス修飾子には以下のものがあります。

private
同じクラス内からのみアクセス可能。
protected
同じクラスおよび継承クラスからのみアクセス可能。
internal
同じアセンブリからのみアクセス可能
(同じプログラム内からのみ、と考えてOK)
public
どこからでもアクセス可能
protected internal
protected + internalの領域

継承などはまだ説明していないので、今はprivatepublicだけ覚えれば良いです。
internalは他のプログラムと連携させる場合に、外部に公開したくないデータに付けます。
(今は気にする必要はありません)

例として、上記のSimpleClassのTestMethodメソッドからpublicを削除してみます。


class SimpleClass
{
    void TestMethod()
    {
        Console.WriteLine("TestMethod実行");
    }
}

static void Main(string[] args)
{
    //SimpleClassのインスタンス生成
    SimpleClass sc = new SimpleClass();
    
    //「TestMethod」がないと怒られる
    sc.TestMethod();
}

SimpleClassのインスタンスであるscからTestMethodを呼び出そうとしても、そんなメソッドはないとエラーになってしまいます。
クラスのフィールドやメソッドはアクセス修飾子を付けない場合はprivateが設定されます。
privateメソッドはそのクラスの外側からはアクセスできないので、Mainメソッドからは呼び出せなくなるのです。

privateメソッドは同じクラス内からであれば呼び出せますから、以下のコードはOKです。


class SimpleClass
{
    public void TestMethodA()
    {
        Console.WriteLine("TestMethodA実行");

        //同じクラス内のprivateメソッドを実行
        TestMethodB();
    }

    private void TestMethodB()
    {
        Console.WriteLine("TestMethodB実行");
    }
}

static void Main(string[] args)
{
    SimpleClass sc = new SimpleClass();
    
    sc.TestMethodA();

    Console.ReadLine();
}
TestMethodA実行
TestMethodB実行

アクセス修飾子はメソッドのほか、フィールドやプロパティ、クラス自体にも付けることができます。
(プロパティについては未説明)


private class SimpleClass
{
    //何も付けないのはprivate
    int num1;

    //フィールド毎に指定できる
    private int num2;
    public string Str;
}

今まで自作メソッド名の前につけていたstaticはアクセス修飾子ではありません。
これの意味はstaticの項で説明します。

内部クラス

Visual StudioでC#プロジェクトを作成すると、以下のようなコードが自動的に生成されます。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

これを見てわかる通り、自動的に「Program」という名称のクラスが作成され、その中にMainメソッドがあります。
今までの解説で作ってきた自作メソッドなども実際にはProgramクラスのメソッドやフィールドを作っていたわけです。

ということは、このページの解説で作成したSimpleClassは、Programクラス内に定義されたクラス(内部クラス)ということになります。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        class SimpleClass
        {
            public void TestMethod()
            {
            }
        }

        static void Main(string[] args)
        {
        }
    }
}

クラスは内部にいくらでもクラスを定義することができます。

クラスのアクセス修飾子

classにもアクセス修飾子を付けることができます。
何も指定しないとprivateになるのは同じです。

内部クラスを外部からも使用したい場合はpublicを指定します。
内部クラスへのアクセスは「外部クラス名.内部クラス名」という形式で行います。


private class SimpleClass
{
    //外部からアクセス不可
    class InnerClassA
    {
        public void Func()
        {
            Console.WriteLine("InnerClassA");
        }
    }

    //外部からアクセス可能
    public class InnerClassB
    {
        public void Func()
        {
            Console.WriteLine("InnerClassB");
        }
    }

}

static void Main(string[] args)
{
    //アクセス不可
    //var innerA = new SimpleClass.InnerClassA();

    var innerB = new SimpleClass.InnerClassB();
    innerB.Func();

    Console.ReadLine();
}

this

クラスのメソッドを以下のように定義してみます。


private class SimpleClass
{
    string firstName;
    string lastName;

    public void Test(string firstName, string lastName)
    {
        firstName = firstName;
        lastName = lastName;
    }
}

このコードのTestメソッド内に記述されている「firstName」は「フィールド」なのでしょうか。
それとも「引数」なのでしょうか。

この場合、firstNameは引数を意味します。
つまり上記のコードは引数を同じ引数に代入しているだけの意味のない処理となります。
(フィールドには何も代入されません)

フィールドと引数の名前が同じになると、そのメソッド内からはフィールドにアクセスできなくなります。
(最も内側のブロックで定義されたものが有効になります)
フィールド名か引数名かのどちらかの名前を変えれば良いのですが、引数やフィールドの名前というのはコードの読みやすさに関わってくるので、あまり変更したくない場合もあります。

名前の衝突を防ぐために、フィールド名の先頭にハイフン(_)を付ける、「m_」という接頭語を付ける、といったルールも決めてしまうのも一つの方法です。
(「m」というのは「メンバー」の意味)
その他、thisというキーワードを用いる方法もあります。


private class SimpleClass
{
    string firstName;
    string lastName;

    public void Test(string firstName, string lastName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

このようにすると「this」が付いているものはフィールド(またはプロパティ)である、という意味になります。

厳密に言えば、「this」が指しているものは「自分自身」であり「自分」とは「インスタンス」のことです。
thisは呼び出し元のインスタンスへの参照です。
「this.○○」は、そのメソッドを呼び出したインスタンスが内部に持っているデータにアクセスする、という意味になります。


private class SimpleClass
{
    string str = "なし";
    int num;

    public void Test(string str)
    {
        //引数「str」にアクセス
        Console.WriteLine(str);

        //メソッドを呼び出したインスタンスが内部に持っている
        //「str」にアクセス
        Console.WriteLine(this.str);

        //引数と名前が衝突していなくても
        //thisキーワードは付けられる
        this.num = 10;
    }
}

static void Main(string[] args)
{
    SimpleClass sc = new SimpleClass();

    //インスタンスscは内部に
    //str="なし", num=0
    //というデータを持っている

    //インスタンスからメソッドを呼び出す
    sc.Test("aaa");

    Console.ReadLine();
}
aaa
なし