C#idiomatic C#
objectscsharp/fundamentals/object-oriented

Language

Objects - create instances of types

C# uses a class or struct definition to define types of objects. In an object-oriented language such as C#, a program consists of objects interacting dynamically.

  • page csharp/fundamentals/object-oriented/objects
  • updated 10/13/2025

A class or struct definition is like a blueprint that specifies what the type can do. An object is a block of memory that the program allocates and configures according to the blueprint. A program might create many objects of the same class. You can also call objects instances. You can store them in a named variable or in an array or collection. Client code uses these variables to call the methods and access the public properties of the object. In an object-oriented language such as C#, a typical program consists of multiple objects interacting dynamically.

Struct instances vs. class instances

Because classes are reference types, a variable of a class object holds a reference to the address of the object on the managed heap. If you assign a second variable of the same type to the first variable, both variables refer to the object at that address. This article discusses this point in more detail later.

You create instances of classes by using the new operator. In the following example, Person is the type and person1 and person2 are instances, or objects, of that type.

sample · csharpdocs/csharp/fundamentals/object-oriented/snippets/objects/Program.cs

csharp
using System;

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

class Program
{
    static void Main()
    {
        Person person1 = new("Leopold", 6);
        Console.WriteLine($"person1 Name = {person1.Name} Age = {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 = {person2.Name} Age = {person2.Age}");
        Console.WriteLine($"person1 Name = {person1.Name} Age = {person1.Age}");

        /*
            Output:
            person1 Name = Leopold Age = 6
            person2 Name = Molly Age = 16
            person1 Name = Molly Age = 16
        */
    }
}

Because structs are value types, a variable of a struct object holds a copy of the entire object. You can also create instances of structs by using the new operator, but you don't need to use it, as shown in the following example:

sample · csharpdocs/csharp/fundamentals/object-oriented/snippets/objects/Application.cs

csharp
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("Alex", 9);
            Console.WriteLine($"p1 Name = {p1.Name} Age = {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 = {p2.Name} Age = {p2.Age}");

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

The thread stack allocates memory for both p1 and p2. The program reclaims that memory along with the type or method in which you declare it. This memory management is one reason why structs are copied on assignment. By contrast, the common language runtime automatically reclaims (garbage collects) the memory it allocates for a class instance when all references to the object go out of scope. You can't deterministically destroy a class object like you can in C++. For more information about garbage collection in .NET, see Garbage Collection.

Object identity vs. value equality

When you compare two objects for equality, first decide whether you want to know if the two variables represent the same object in memory or if the values of one or more of their fields are equivalent. If you want to compare values, consider whether the objects are instances of value types (structs) or reference types (classes, delegates, arrays).

  • Use the static System.Object.ReferenceEquals method to determine whether two class instances refer to the same location in memory (which means that they have the same identity). (System.Object is the implicit base class for all value types and reference types, including user-defined structs and classes.)
  • By default, the System.ValueType.Equals method determines whether the instance fields in two struct instances have the same values. Because all structs implicitly inherit from System.ValueType, you call the method directly on your object as shown in the following example:

sample · csharpdocs/csharp/fundamentals/object-oriented/snippets/objects/Equality.cs

csharp
using System;

public class Equality
{
    public static void EqualityTest()
    {
        //<Snippet32>
        // Person is defined in the previous example.

        //public struct Person(string name, int age)
        //{
        //    public string Name { get; set; } = name;
        //    public int Age { get; set; } = age;
        //}

        Person p1 = new("Wallace", 75);
        Person p2 = new("", 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.
        //</Snippet32>
    }
}

The default System.ValueType implementation of Equals uses boxing and reflection in some cases. For information about how to provide an efficient equality algorithm that's specific to your type, see Implement equality yourself when a type can't be a record. Records are reference types that use value semantics for equality.

For more information, see: