34
26

More than 5 years have passed since last update.

C#でクラスのインスタンスが初期化されてない

Last updated at Posted at 2015-02-08

普段はC++を使っていながら最近C#を使い出してハマったところを共有したいと思います。

何にハマったかというと、
クラスのインスタンスを配列で定義した時に、サイズ指定しかされずに初期化されない
という現象が起こりました。

この現象について、それぞれの言語での実装例から説明したいと思います。
以下のサンプルは、自作クラスのインスタンスを配列で定義して中身を表示しています。

C++の場合

main.cpp

#include "stdio.h"
#include <string>

class Person
{
public:
    int m_old;
    std::string m_name;

    Person(){
        m_old = 10;
        m_name = std::string("Hoge");
    }
};

void main()
{
    Person hoges[10];
    for (int i = 0; i < 10; ++i)
    {
        printf("%d : ", i);
        printf("Old = %d  ", hoges[i].m_old);
        printf("Name = %s\n", hoges[i].m_name.c_str());
    }
}

実行すると、以下のように各インスタンスが初期化された状態で表示できます。

0 : Old = 10 Name = Hoge
1 : Old = 10 Name = Hoge
2 : Old = 10 Name = Hoge
3 : Old = 10 Name = Hoge
4 : Old = 10 Name = Hoge
5 : Old = 10 Name = Hoge
6 : Old = 10 Name = Hoge
7 : Old = 10 Name = Hoge
8 : Old = 10 Name = Hoge
9 : Old = 10 Name = Hoge

C#の場合

同様にC#でも同じ挙動をさせてみます。

main_error.cs

using System;

namespace Sample01CSharp
{
    public class Person
    {
        public int m_old;
        public string m_name;

        public Person(){
            m_old = 10;
            m_name = "Hoge";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person[] hoges = new Person[10];
            for (int i = 0; i < 10; ++i){
                Console.WriteLine(i + " : Old = " + hoges[i].m_old
                             + "  Names = " + hoges[i].m_name);
         }
        }
    }
}

実行するとNull参照のエラーが発生します。

ハンドルされていない例外: System.NullReferenceException: オブジェクト参照がオブ
ジェクト インスタンスに設定されていません。

これは、配列を初期化した場合に各要素に対してコンストラクタが呼び出されないことが原因です。
ただサイズを確保してくれるだけなので、以下のように変更して、確実にコンストラクタを呼ぶようにします。

main.cs

using System;

namespace Sample01CSharp
{
    public class Person
    {
        public int m_old;
        public string m_name;

        public Person(){
            m_old = 10;
            m_name = "Hoge";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person[] hoges = new Person[10];

            for (int i = 0; i < 10; ++i)
                hoges[i] = new Person(); //各要素のコンストラクタを明示的に呼び出す

            for (int i = 0; i < 10; ++i){
                Console.WriteLine(i + " : Old = " + hoges[i].m_old
                             + "  Names = " + hoges[i].m_name);
         }
        }
    }
}



この変更によって、初期化できてないエラーがでなくなり、C++と同じ結果が得られます。
ほとんどがNull参照エラーになって気づけると思うですが、
C++の作法に慣れすぎていると理由が分からなくて時間を取ってしまうこともあるので注意が必要です。

その他

クラスのインスタンスを一つしか使わない、Listなどを使って保持する場合は顕在化しないことが多いので、今更ながらハマったような気がします。

自分の場合はC++のDLLをUnityで使うためにC#のプラグインを書いていたので、
C++の構造体と同じような配置にしたいがために、オブジェクトの配列を使わざるを得なかったという状況があります。

一応、上記のコードをgithubにも載せましたので使いたい人いればアクセスしてみてください。
https://github.com/scnsh/Samples

以上、どなたかの助けになれば幸いです。

34
26
2

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
34
26