閉じる

C#のnewでインスタンス化する基本とよくあるエラー対策

C#でオブジェクトを使い始めるときに必ず通るのが、newでのインスタンス化です。

本記事では、newの基本から、配列や引数付きの生成パターン、そして初心者がつまずきやすいコンパイルエラーと対処法までを段階的に解説します。

最後に日々の実装で役立つチェックポイントもまとめます。

C#のnewによるインスタンス化の基本

インスタンスとクラスの関係

クラスは設計図インスタンスはその設計図から作られる実体です。

C#ではclassで設計図を定義し、newで実体を作ります。

プロパティを通じてデータを保持し、メソッドで振る舞いを与えます。

以下は、Personクラスを定義してインスタンス化する最小の例です。

C#
using System;

public class Person
{
    // 自動実装プロパティ(ゲッターとセッター)
    public string Name { get; set; }
    public int Age { get; set; }

    // 引数なしコンストラクタ(初期値を設定)
    public Person()
    {
        Name = "unknown";
        Age = 0;
    }

    // インスタンスメソッド(自己紹介)
    public void Greet()
    {
        Console.WriteLine($"こんにちは、{Name}です。{Age}歳です。");
    }
}

public class Program
{
    public static void Main()
    {
        // newでPersonのインスタンスを作成
        Person p = new Person();
        // プロパティを設定
        p.Name = "Taro";
        p.Age = 20;
        // メソッドを呼び出し
        p.Greet();
    }
}
実行結果
こんにちは、Taroです。20歳です。

補足

  • Person p;だけではインスタンスは作られません。pは参照を入れる箱に過ぎず、new Person()で初めて中身が用意されます。
  • 1つのクラスから何個でもインスタンスを作れます。それぞれは独立したデータを持ちます。

new演算子の役割とインスタンスの寿命

newは主に次の役割を持ちます。

  • 管理ヒープにメモリを確保し、クラスの初期化処理(コンストラクタ)を実行する。
  • そのオブジェクトへの参照を返す。

C#のガベージコレクタ(GC)が、到達不能になったインスタンスを自動的に回収します。

重要なのは「変数の寿命」と「オブジェクトの寿命」は一致しないことです。

変数のスコープを抜けても、他の場所に参照が残っていればオブジェクトは生き続けます。

C#
using System;

public class Person
{
    public string Name { get; set; } = "NoName";
}

public class Program
{
    // Personを生成して返すヘルパー
    static Person CreatePerson(string name)
    {
        // newはヒープにオブジェクトを作り、その参照を返す
        return new Person { Name = name };
    }

    public static void Main()
    {
        Person outer = CreatePerson("Hanako"); // CreatePersonのローカル変数は破棄されるが、オブジェクトはouterが参照しているので生存
        Console.WriteLine(outer.Name);

        // outerがnullになり、他に誰も参照していなければ、いずれGCで回収される
        outer = null;
        Console.WriteLine("参照を手放しました。必要ならGCが後で回収します。");
    }
}
実行結果
Hanako
参照を手放しました。必要ならGCが後で回収します。

newが必要な型(参照型)と不要な型(値型)

C#には大きく値型と参照型があります。

インスタンス化ではこの違いを知っておくと混乱しません。

  • 参照型はnewでヒープ上にオブジェクトを作ります。
  • 値型はnewなしでも変数自体が実体です。newを付けると「既定値で初期化して使う」意味になります。

次の表は代表例です。

種別newの要否
参照型class, array, string, List<T>, Dictionary<TKey,TValue> など必要(ただしstringはリテラルで生成されることも多い)
値型int, double, bool, struct, enum, DateTime など不要(使う前に値を代入)。newを付けると既定値で初期化
C#
using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        // 値型(int)はnew不要
        int a = 123;
        // newを付けると0で初期化される
        int b = new int(); // 0

        // 参照型(string)はリテラルでも生成できる(実際はランタイムが用意)
        string s1 = "Hello";
        // newで生成することもある(例: 文字の繰り返し)
        string s2 = new string('A', 5); // "AAAAA"

        // 参照型(List<T>)はnewが必要
        List<int> nums = new List<int>();

        Console.WriteLine($"a={a}, b={b}, s1={s1}, s2={s2}, nums.Count={nums.Count}");
    }
}
実行結果
a=123, b=0, s1=Hello, s2=AAAAA, nums.Count=0

newの書き方とオブジェクト生成パターン

new クラス名() の基本構文

最も基本的な構文はnew クラス名()です。

プロパティの初期化には代入かオブジェクト初期化子を使います。

C#
using System;

public class Person
{
    public string Name { get; set; } = "unknown";
    public int Age { get; set; } = 0;
}

public class Program
{
    public static void Main()
    {
        // 1. 生成してから代入
        Person p1 = new Person();
        p1.Name = "Miki";
        p1.Age = 18;

        // 2. オブジェクト初期化子でまとめて
        Person p2 = new Person { Name = "Ken", Age = 25 };

        // 3. C# 9以降: 変数の型が明らかな場合は target-typed new
        Person p3 = new() { Name = "Sara", Age = 30 };

        Console.WriteLine($"{p1.Name}({p1.Age}) / {p2.Name}({p2.Age}) / {p3.Name}({p3.Age})");
    }
}
実行結果
Miki(18) / Ken(25) / Sara(30)

メモ

  • オブジェクト初期化子は、必須項目をコンストラクタで、任意項目を初期化子で、のように使い分けると読みやすくなります。
  • varは右辺から型が推論できる場合に使えますが、学習初期は明示的な型を書くと混乱しにくいです。

引数付きnew(コンストラクタ呼び出し)

コンストラクタに引数を持たせると、生成時に必須の情報を渡せます。

C#
using System;

public class Person
{
    public string Name { get; }
    public int Age { get; }

    // 引数付きコンストラクタ(必須情報を強制できる)
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public override string ToString() => $"{Name}({Age})";
}

public class Program
{
    public static void Main()
    {
        // 引数付きのnewで生成
        var p = new Person("Akira", 22);
        Console.WriteLine(p);
    }
}
実行結果
Akira(22)

ヒント

  • コンストラクタを複数定義して「最低限必要な引数の組み合わせ」を提供すると、生成時のミスを減らせます。
  • オプション値はオブジェクト初期化子と併用すると柔軟です。

配列をnewで生成する

配列はnew 型[長さ]で生成します。

値型配列の各要素は既定値で初期化されますが、参照型配列は各要素がnullで入ります。

C#
using System;

public class Person
{
    public string Name { get; set; } = "unknown";
    public override string ToString() => Name;
}

public class Program
{
    public static void Main()
    {
        // 値型の配列は既定値(0など)で埋まる
        int[] numbers = new int[3]; // {0,0,0}
        Console.WriteLine(string.Join(",", numbers)); // 0,0,0

        // 参照型の配列は要素がnull
        Person[] people = new Person[2]; // {null, null}

        // nullが入っていることを確認
        Console.WriteLine(people[0] == null ? "people[0] is null" : "not null");

        // 各要素をnewしてから使う
        people[0] = new Person { Name = "Taro" };
        people[1] = new Person { Name = "Hanako" };

        // オーバーロード解決のため、object[]に強制キャスト
        Console.WriteLine(string.Join(", ", (object[])people));

    }
}
実行結果
0,0,0
people[0] is null
Taro, Hanako

C#のnewでよくあるエラーと対処法

CS0246: 型や名前空間が見つからない

原因は主に次の2つです。

対象の名前空間をusingしていない、または参照(パッケージ/プロジェクト)を追加していないことです。

C#
// NG: List<T>の名前空間をusingしていない
public class Program
{
    public static void Main()
    {
        List<string> list = new List<string>(); // CS0246
    }
}
実行結果
error CS0246: 型または名前空間名 'List<>' が見つかりません(using ディレクティブまたはアセンブリ参照が不足しています)。

対処例は次の通りです。

C#
// OK: usingを追加
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        List<string> list = new List<string>();
    }
}
C#
// OK: 完全修飾名を使う(using不要)
public class Program
{
    public static void Main()
    {
        System.Collections.Generic.List<string> list = new System.Collections.Generic.List<string>();
    }
}

補足

NuGetパッケージが必要な型は、パッケージの参照がなければCS0246になります。参照の追加も確認してください。

CS1729: コンストラクタが見つからない/引数が合わない

存在しない引数の組み合わせでnewしたときに出ます。

C#
public class Car
{
    public string Model { get; }
    public Car(string model) { Model = model; }
}

public class Program
{
    public static void Main()
    {
        var car = new Car(); // CS1729: 引数0のコンストラクタがない
    }
}
実行結果
error CS1729: 'Car' は引数 0 を指定して構築できません

対処は「正しい引数で呼ぶ」か「必要なら引数なしコンストラクタを追加する」です。

C#
// 対処1: 既存のコンストラクタに合わせて引数を渡す
var car = new Car("Prius");
C#
// 対処2: 引数なしコンストラクタを用意する
public class Car
{
    public string Model { get; }
    public Car() { Model = "unknown"; }
    public Car(string model) { Model = model; }
}

CS0144: 抽象クラスやinterfaceはnewできない

abstract classinterfaceは契約や共通動作の型であり、そのままでは実体化できません。

C#
public abstract class Animal { public abstract void Speak(); }
public interface ILogger { void Log(string message); }

public class Program
{
    public static void Main()
    {
        var a = new Animal();   // CS0144
        var l = new ILogger();  // CS0144
    }
}
実行結果
error CS0144: 抽象クラスまたはインターフェイス 'Animal' のインスタンスを作成できません

具象クラスを作ってそれをnewします。

interfaceは実装クラスをnewし、変数の型はinterfaceにすると柔軟です。

C#
using System;

public abstract class Animal { public abstract void Speak(); }
public class Dog : Animal { public override void Speak() => Console.WriteLine("Bow!"); }

public interface ILogger { void Log(string message); }
public class ConsoleLogger : ILogger { public void Log(string message) => Console.WriteLine(message); }

public class Program
{
    public static void Main()
    {
        Animal dog = new Dog();
        dog.Speak();

        ILogger logger = new ConsoleLogger();
        logger.Log("Hello");
    }
}
実行結果
Bow!
Hello

CS0712: 静的クラスはnewできない

static classはインスタンスを持ちません。

C#
public static class MathUtil
{
    public static int Add(int a, int b) => a + b;
}

public class Program
{
    public static void Main()
    {
        var m = new MathUtil(); // CS0712
    }
}
実行結果
error CS0712: 静的クラス 'MathUtil' のインスタンスを作成できません

静的メンバーはクラス名から直接呼びます。

C#
int sum = MathUtil.Add(1, 2); // OK

CS0122: アクセス修飾子が原因でnewできない

クラスやコンストラクタがprivateinternalなどでアクセス不可な場合に出ます。

よくあるのはコンストラクタがprivateで外からnewできないケースです。

C#
public class User
{
    public string Name { get; }
    private User(string name) { Name = name; } // 外部からは呼べない
}

public class Program
{
    public static void Main()
    {
        var u = new User("Alice"); // CS0122
    }
}
実行結果
error CS0122: 'User.User(string)' は保護レベルのためアクセスできません

対処は「アクセス可能なコンストラクタを公開する」か「公開されたファクトリメソッドを使う」です。

C#
// 対処1: publicコンストラクタを用意
public class User
{
    public string Name { get; }
    public User(string name) { Name = name; }
}
C#
// 対処2: ファクトリに限定
public class User
{
    public string Name { get; }
    private User(string name) { Name = name; }
    public static User Create(string name) => new User(name); // これだけ公開
}

CS0120: インスタンスなしでメンバーを呼ぶエラー

インスタンスメンバーをクラス名から直接呼ぶと発生します。

C#
public class Counter
{
    public int Value { get; private set; }
    public void Inc() => Value++;
}

public class Program
{
    public static void Main()
    {
        Counter.Inc(); // CS0120: インスタンスが必要
    }
}
実行結果
error CS0120: 非静的なフィールド、メソッド、またはプロパティ 'Counter.Inc()' にオブジェクト参照が必要です

newでインスタンスを作ってから呼びましょう。

あるいは設計上インスタンス不要ならstaticにします。

C#
// 対処1: インスタンスを作って呼ぶ
var c = new Counter();
c.Inc();
C#
// 対処2: 静的メンバーにする(設計上インスタンス不要な場合のみ)
public class Counter
{
    public static int Total { get; private set; }
    public static void Inc() => Total++;
}

NullReferenceExceptionはnew忘れに注意

実行時によく遭遇するのがNullReferenceExceptionです。

参照型の変数がnullなのにメンバーへアクセスしたときに発生します。

配列の各要素がnullである点は特に注意です。

C#
using System;

public class Person
{
    public string Name { get; set; } = "";
}

public class Program
{
    public static void Main()
    {
        // 参照型配列の各要素はnullで初期化される
        Person[] people = new Person[1];
        // 下行はpeople[0]がnullのため実行時例外
        Console.WriteLine(people[0].Name);
    }
}
実行結果
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.

正しくは各要素をnewしてから使います。

安全に扱う補助として?.??演算子も役立ちます。

C#
// 正しい初期化
Person[] people = new Person[1];
people[0] = new Person { Name = "Miki" };
Console.WriteLine(people[0].Name); // OK

// 安全にアクセス(people[0]がnullなら"unknown"を使う)
string name = people[0]?.Name ?? "unknown";

インスタンス化のベストプラクティス

new前に型と名前空間を確認する

インスタンス化でCS0246が出るときは、まず対象型がどの名前空間かを確認し、usingがあるか、必要な参照(NuGetパッケージやプロジェクト参照)が追加されているかを見直します。

迷ったら一時的に完全修飾名(例: System.Text.StringBuilder)で書くと原因の切り分けができます。

コンストラクタ引数と既定値を確認する

コンストラクタのシグネチャに合った引数が必要です。

最低限の必須情報はコンストラクタで受け取り、任意の設定はオブジェクト初期化子で与えると読みやすくなります。

引数が多い場合は名前付き引数を使うと見間違いを防げます。

C#
var user = new Person(name: "Akira", age: 22); // 名前付き引数で可読性向上

参照がnullでないことを確認する

参照型を使う前には必ずnewしているかを確認します。

メソッドの先頭でガード節を置くのは有効です。

C#
void Print(Person person)
{
    if (person == null) throw new ArgumentNullException(nameof(person));
    Console.WriteLine(person.Name);
}

状況に応じて?.??を使い、NullReferenceExceptionを未然に防ぎます。

ただし!(null許容性抑止)は最後の手段で、正しく初期化する設計が優先です。

まとめ

newはC#でインスタンスを作るための基本中の基本です。

クラスは設計図、newは実体化、参照型はnewが必要、値型はnew不要という整理が第一歩になります。

配列では参照型要素がnullである点を忘れず、各要素をnewしてから使うことが重要です。

よくあるエラーは、名前空間の不足、コンストラクタの不一致、抽象型や静的クラスの誤用、アクセス修飾子の制約、インスタンスなし呼び出し、そしてnull参照が原因です。

生成時は型と名前空間、コンストラクタの引数、nullの可能性を順に確認すれば、多くのつまずきを避けられます。

実装では「必須情報はコンストラクタ、任意は初期化子」「使う前にnew」「nullは早めに検知」の方針を徹底して、安全で読みやすいコードを目指しましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

C#の入門記事を中心に、開発環境の準備からオブジェクト指向の基本まで、順を追って解説しています。ゲーム開発や業務アプリを目指す人にも役立ちます。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!