閉じる

C#のnewとは?インスタンス生成の仕組みと使い方

C#でクラスを学び始めると、必ず登場するのがnewキーワードです。

なんとなく「インスタンスを作るもの」という印象はあっても、どんな仕組みでメモリが確保され、どこでコンストラクタが呼ばれ、どんな場面で省略できたりできなかったりするのかは、意外とあいまいになりがちです。

本記事では、newによるインスタンス生成の基本から、実際のメモリのイメージ、使い方のパターンまでを、図解とサンプルコードで詳しく解説します。

C#のnewとは何か

newキーワードの役割

newは「インスタンスを作るための演算子」です

クラスや構造体などの型から、実際に使える「モノ」(オブジェクト)をメモリ上に作成します。

C#では、次のような役割を持ちます。

  • ヒープ(またはスタック)上にメモリを確保する
  • フィールドを既定値で初期化する
  • コンストラクタを呼び出す
  • そのオブジェクトへの参照を返す

基本的な書き方

もっとも基本的な書き方は次の形です。

C#
クラス名 変数名 = new クラス名(引数...);

例として、Personクラスのインスタンスを生成するコードを見てみます。

C#
Person p = new Person("Taro", 20);

この1行で、Person型のオブジェクトがヒープ上に作られ、その場所を指す参照が変数pに格納される、という処理が行われています。

インスタンス生成の流れを図解で理解する

メモリ上で何が起きているか

インスタンス生成は、次のようなステップで進みます。

  1. ヒープ領域に必要なサイズのメモリが確保される
  2. その型のフィールドが既定値(数値なら0、参照ならnull、boolならfalseなど)に初期化される
  3. 対応するコンストラクタが呼ばれ、初期化ロジックが実行される
  4. 生成されたオブジェクトのアドレス(参照)が、左辺の変数に代入される

変数自体はスタックにあり、中身として「ヒープ上のオブジェクトの場所」を保持する、というイメージを持つと理解しやすくなります。

newの基本的な使い方

クラスのインスタンス生成

まずは最もよく使うパターンです。

C#
using System;

class Person
{
    public string Name;
    public int Age;

    // 引数なしコンストラクタ
    public Person()
    {
        Name = "NoName";
        Age = 0;
    }

    // 引数ありコンストラクタ
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public void Introduce()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

class Program
{
    static void Main()
    {
        // 引数なしコンストラクタを使ったインスタンス生成
        Person p1 = new Person();
        p1.Introduce();

        // 引数ありコンストラクタを使ったインスタンス生成
        Person p2 = new Person("Taro", 20);
        p2.Introduce();
    }
}
実行結果
Name: NoName, Age: 0
Name: Taro, Age: 20

ここでnew Person()new Person(“Taro”, 20)が、それぞれ異なるコンストラクタを呼び出しています。

配列のインスタンス生成

配列も、newによるインスタンス生成が必要なオブジェクトです。

C#
using System;

class Program
{
    static void Main()
    {
        // int型の配列を長さ5で生成
        int[] numbers = new int[5];

        // すべて0で初期化されている
        for (int i = 0; i < numbers.Length; i++)
        {
            Console.WriteLine(numbers[i]);
        }

        // 文字列配列を初期化と同時に生成
        string[] names = new string[] { "A", "B", "C" };

        foreach (string name in names)
        {
            Console.WriteLine(name);
        }
    }
}
実行結果
0
0
0
0
0
A
B
C

配列のnew int[5]のように、型と要素数を指定する形も、インスタンス生成の一種です。

コンストラクタとnewの関係

newが呼び出すコンストラクタ

newは必ずコンストラクタを呼び出します

コンストラクタは、オブジェクト生成時に1回だけ実行される特別なメソッドです。

C#
class Sample
{
    public int Value;

    // コンストラクタ(引数なし)
    public Sample()
    {
        Value = 10;
    }

    // コンストラクタ(引数あり)
    public Sample(int initialValue)
    {
        Value = initialValue;
    }
}

class Program
{
    static void Main()
    {
        // 引数なしコンストラクタが呼ばれる
        Sample s1 = new Sample();
        Console.WriteLine(s1.Value); // 10

        // 引数ありコンストラクタが呼ばれる
        Sample s2 = new Sample(99);
        Console.WriteLine(s2.Value); // 99
    }
}
実行結果
10
99

どのコンストラクタが呼ばれるかは、newの後ろに書く引数の型と数で決まる点が重要です。

値型と参照型でのnewの違い

参照型(クラス)の場合

クラスは参照型であり、newによりヒープ上にオブジェクトが作られます。

変数はその場所を指す参照を保持します。

値型(構造体)の場合

構造体は値型であり、通常はスタック上に値そのものが置かれます。

値型にもnewを使う場合と使わない場合があります。

C#
using System;

struct Point
{
    public int X;
    public int Y;
}

class Program
{
    static void Main()
    {
        // 値型にnewを使う例
        Point p1 = new Point(); // フィールドは0で初期化される
        Console.WriteLine($"p1: X={p1.X}, Y={p1.Y}");

        // newを使わない場合、すべてのフィールドを手動で初期化する必要がある
        Point p2;
        p2.X = 10;
        p2.Y = 20;
        Console.WriteLine($"p2: X={p2.X}, Y={p2.Y}");
    }
}
実行結果
p1: X=0, Y=0
p2: X=10, Y=20

値型でもnewを使うと、フィールドが既定値で初期化されるので、安全に扱えるようになります。

ただし、値型のnewはヒープではなく、値そのものを初期化している点が参照型と異なります。

newを省略できるケース

型推論(var)でもnewは必要

C#にはvarによる型推論がありますが、varは左辺の型指定を省略するだけで、newそのものは省略できません

C#
using System;

class Person
{
    public string Name;
}

class Program
{
    static void Main()
    {
        // 明示的な型指定
        Person p1 = new Person();

        // varによる型推論(右辺のnew Person()を見てPerson型と推論)
        var p2 = new Person();

        Console.WriteLine(p2.GetType().Name); // Person
    }
}
実行結果
Person

コレクション初期化子との組み合わせ

C#では、newと初期化子を組み合わせることで、すっきりとした記述ができます

C#
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // List<int>を生成し、初期値をまとめて指定
        List<int> numbers = new List<int> { 1, 2, 3, 4 };

        foreach (var n in numbers)
        {
            Console.WriteLine(n);
        }
    }
}
実行結果
1
2
3
4

newのいろいろな使いどころ

無名型(anonymous type)の生成

LINQなどでよく使われる無名型も、実はnewで生成しています。

C#
using System;

class Program
{
    static void Main()
    {
        // new { プロパティ = 値 } の形で無名型のインスタンスを生成
        var person = new { Name = "Taro", Age = 20 };

        Console.WriteLine($"{person.Name}, {person.Age}");
    }
}
実行結果
Taro, 20

型名は書けませんが、コンパイラが内部でクラスを生成し、newでインスタンスを作っているイメージです。

new演算子とnew修飾子の違いに注意

C#には、newという単語を「演算子」として使う場合と、「修飾子」として使う場合があります。

本記事のテーマは前者ですが、後者も名前が同じなので混同しがちです。

C#
class Base
{
    public void Show()
    {
        Console.WriteLine("Base.Show");
    }
}

class Derived : Base
{
    // new修飾子: 基底クラスの同名メンバーを隠す
    public new void Show()
    {
        Console.WriteLine("Derived.Show");
    }
}

このnew void Show()newインスタンス生成とは関係がありません

「同名メンバーを隠す」ための修飾子として使われています。

newによるインスタンス生成の注意点

生成しすぎによるメモリ消費

newでインスタンスを作るということは、その分だけメモリを占有することになります。

C#ではガベージコレクタ(GC)が不要になったオブジェクトを自動で回収しますが、大量のオブジェクトを頻繁にnewし続けると、GCのコストが増えパフォーマンス低下の原因になります。

特に以下のようなケースでは注意が必要です。

  • 毎フレーム大量のオブジェクトを生成するゲームやリアルタイム処理
  • 高頻度のループ内で短命なオブジェクトを何度もnewしているコード

インスタンス生成が必要な場面/不要な場面

staticメンバーはインスタンス生成なしで使える、という点も合わせて理解しておくと混乱が減ります。

C#
using System;

class Util
{
    public static void PrintHello()
    {
        Console.WriteLine("Hello");
    }
}

class Program
{
    static void Main()
    {
        // staticメソッドはインスタンス生成(new)なしで呼べる
        Util.PrintHello();

        // インスタンスメソッドを呼ぶにはnewが必要
        Person p = new Person();
        p.Name = "Taro";
        // p.Introduce() のように使用
    }
}

class Person
{
    public string Name;
    public void Introduce()
    {
        Console.WriteLine($"I am {Name}");
    }
}
実行結果
Hello

「状態を持たないユーティリティ処理」はstaticで、「個別の状態を持つもの」はインスタンスとnewという使い分けが基本になります。

まとめ

newは、C#におけるインスタンス生成の中核となるキーワードです。

クラスや配列、値型などに対してメモリを確保し、フィールドを初期化し、コンストラクタを呼び出して、使用可能なオブジェクトを作り出します。

参照型と値型ではnewの意味合いが少し異なり、varやコレクション初期化子と組み合わせることで、より読みやすいコードを書くことができます。

また、同じnewでも「修飾子」として使われる場合もあるため、文脈に注意することが大切です。

インスタンス生成の仕組みを理解することで、メモリのイメージや設計の判断がしやすくなり、C#のコードがぐっと扱いやすくなります。

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

URLをコピーしました!