オブジェクト - 型のインスタンスを作成する

クラスまたは構造体の定義は、型の動作を指定する設計図に似ています。 オブジェクトは基本的に、設計図に従って割り当てられて構成されたメモリのブロックです。 プログラムでは、同じクラスのオブジェクトを多数作成できます。 オブジェクトはインスタンスとも呼ばれ、名前付きの変数または配列やコレクションに格納できます。 クライアント コードとは、これらの変数を使ってメソッドを呼び出し、オブジェクトのパブリック プロパティにアクセスするコードです。 C# などのオブジェクト指向言語では、一般的なプログラムは動的に対話する複数のオブジェクトで構成されています。

注意

静的な型の動作方法は、ここで説明する動作方法とは異なります。 詳細については、「静的クラスと静的クラス メンバー」を参照してください。

構造体インスタンスとクラス インスタンス

クラスは参照型であるため、クラスのオブジェクトの変数は、マネージド ヒープ上のオブジェクトのアドレスへの参照を保持します。 同じ型の 2 番目の変数が最初の変数に代入された場合、両方の変数がそのアドレスにあるオブジェクトを参照します。 この点については、この記事で後ほど詳しく説明します。

クラスのインスタンスは、new 演算子を使って作成されます。 次の例では、Person が型で、person1person2 がその型のインスタンスつまりオブジェクトです。

using System;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
    // Other properties, methods, events...
}

class Program
{
    static void Main()
    {
        Person person1 = new Person("Leopold", 6);
        Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name, person1.Age);

        // Declare new person, assign person1 to it.
        Person person2 = person1;

        // Change the name of person2, and person1 also changes.
        person2.Name = "Molly";
        person2.Age = 16;

        Console.WriteLine("person2 Name = {0} Age = {1}", person2.Name, person2.Age);
        Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name, person1.Age);
    }
}
/*
    Output:
    person1 Name = Leopold Age = 6
    person2 Name = Molly Age = 16
    person1 Name = Molly Age = 16
*/

構造体は値型であるため、構造体オブジェクトの変数はオブジェクト全体のコピーを保持します。 次の例に示されているように、構造体のインスタンスも new 演算子を使って作成できますが、これは必須ではありません。

using System;

namespace Example
{
    public struct Person
    {
        public string Name;
        public int Age;
        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }

    public class Application
    {
        static void Main()
        {
            // Create  struct instance and initialize by using "new".
            // Memory is allocated on thread stack.
            Person p1 = new Person("Alex", 9);
            Console.WriteLine("p1 Name = {0} Age = {1}", p1.Name, p1.Age);

            // Create  new struct object. Note that  struct can be initialized
            // without using "new".
            Person p2 = p1;

            // Assign values to p2 members.
            p2.Name = "Spencer";
            p2.Age = 7;
            Console.WriteLine("p2 Name = {0} Age = {1}", p2.Name, p2.Age);

            // p1 values remain unchanged because p2 is  copy.
            Console.WriteLine("p1 Name = {0} Age = {1}", p1.Name, p1.Age);
        }
    }
    /*
        Output:
        p1 Name = Alex Age = 9
        p2 Name = Spencer Age = 7
        p1 Name = Alex Age = 9
    */
}

p1p2 のメモリはどちらも、スレッドのスタックに割り当てられます。 そのメモリは、それが宣言されている型またはメソッドと共に解放されます。 これは、割り当て時に構造体がコピーされる理由の 1 です。 これに対し、クラスのインスタンスに割り当てられたメモリは、そのオブジェクトに対するすべての参照がスコープ外になると、共通言語ランタイムによって自動的に解放 (ガベージ コレクション) されます。 C++ のようにクラスのオブジェクトを確定的に破棄することはできません。 .NET のガベージ コレクションの詳細については、「ガベージ コレクション」を参照してください。

注意

マネージド ヒープ上のメモリの割り当てと解放は、共通言語ランタイムにおいて高度に最適化されています。 ほとんどの場合、ヒープへのクラス インスタンスの割り当てと、スタックへの構造体インスタンスの割り当てに、パフォーマンス コストの点で大きな違いはありません。

オブジェクト ID と値の等価性

2 つのオブジェクトが等しいかどうかを比較するときは、最初に、2 つの変数がメモリ内の同じオブジェクトを表しているかどうかを知りたいのか、それともオブジェクトの 1 つ以上のフィールドの値が等しいかどうかを知りたいのかを、区別する必要があります。 値を比較する場合は、オブジェクトが値型 (構造体) のインスタンスか、または参照型 (クラス、デリゲート、配列) のインスタンスかを、検討する必要があります。

  • クラスの 2 つのインスタンスがメモリ内の同じ場所を参照しているかどうか (つまり、同じ ID か) を調べるには、静的な Object.Equals メソッドを使います (System.Object は、ユーザー定義の構造体やクラスを含む、すべての値型と参照型の暗黙の基底クラスです)。

  • 2 つの構造体インスタンスのインスタンス フィールドが同じ値を持つかどうかを調べるには、ValueType.Equals メソッドを使います。 すべての構造体は System.ValueType を暗黙的に継承するので、次の例で示すように、オブジェクトで直接メソッドを呼び出します。

    // Person is defined in the previous example.
    
    //public struct Person
    //{
    //    public string Name;
    //    public int Age;
    //    public Person(string name, int age)
    //    {
    //        Name = name;
    //        Age = age;
    //    }
    //}
    
    Person p1 = new Person("Wallace", 75);
    Person p2 = new Person("", 42);
    p2.Name = "Wallace";
    p2.Age = 75;
    
    if (p2.Equals(p1))
        Console.WriteLine("p2 and p1 have the same values.");
    
    // Output: p2 and p1 have the same values.
    

    Equals による System.ValueType の実装では、ボックス化とリフレクションが使用される場合があります。 型に固有の効率的な等価性アルゴリズムを提供する方法の詳細については、型の値の等価性を定義する方法に関するページを参照してください。 レコードは、等価性に値のセマンティクスを使用する参照型です。

  • クラスの 2 つのインスタンスのフィールドの値が等しいかどうかを調べるには、Equals メソッドまたは == 演算子を使用できる場合があります。 ただし、この方法を使用できるのは、その型のオブジェクトにおける "等値" の意味のカスタム定義が、クラスのオーバーライドまたはオーバーロードによって提供されている場合だけです。 クラスは、IEquatable<T> インターフェイスまたは IEqualityComparer<T> インターフェイスを実装することもできます。 どちらのインターフェイスも、値の等価性をテストするために使うことができるメソッドを提供します。 Equals をオーバーライドする独自のクラスを設計するときは、「型の値の等価性を定義する方法」および「Object.Equals(Object)」に記載されているガイドラインに従ってください。

詳細情報