다음을 통해 공유


C# 구조체

팁 (조언)

소프트웨어 개발이 새로운가요? 먼저 시작하기 자습서부터 시작하세요. 코드에 경량 값 형식이 필요하면 구조체가 발생합니다.

다른 언어로 경험하신 적 있나요? C# 구조체는 C++ 또는 Swift의 구조체와 비슷한 값 형식이지만, boxed를 사용하고 인터페이스, 생성자 및 메서드를 지원할 때 관리되는 힙에 있습니다. C#의 특정 패턴을 찾기 위해 읽기 전용 구조체 섹션을 대충 훑어보십시오. 레코드 구조체는 레코드를 참조 하세요.

구조체는 힙의 개체에 대한 참조를 통하지 않고 해당 데이터를 인스턴스에 직접 보유하는 값 형식입니다. 새 변수에 구조체를 할당하면 런타임에서 전체 인스턴스를 복사합니다. 각 변수가 다른 인스턴스를 나타내므로 한 변수에 대한 변경 내용은 다른 변수에 영향을 주지 않습니다. 주 역할이 모델링 동작이 아닌 데이터를 저장하는 작은 경량 형식에 구조체를 사용합니다. 예를 들어 좌표, 색, 측정값 또는 구성 설정이 있습니다.

구조체 선언

키워드를 사용하여 구조체를 정의합니다 struct . 구조체는 클래스와 마찬가지로 필드, 속성, 메서드 및 생성자를 포함할 수 있습니다.

struct Point
{
    public double X { get; set; }
    public double Y { get; set; }

    public readonly double DistanceTo(Point other)
    {
        var dx = X - other.X;
        var dy = Y - other.Y;
        return Math.Sqrt(dx * dx + dy * dy);
    }

    public override string ToString() => $"({X}, {Y})";
}

구조체는 Point 두 값을 저장하고 두 double 점 사이의 거리를 계산하는 메서드를 제공합니다. DistanceTo 이 메서드는 구조체의 상태를 수정하지 않으므로 표시됩니다readonly. 해당 패턴은 읽기 전용 멤버에서 다룹니다.

값 의미 체계

구조체는 값 형식입니다. 할당은 데이터를 복사하므로 각 변수는 고유한 독립 복사본을 보유합니다.

var p1 = new Point { X = 3, Y = 4 };
var p2 = p1; // copies the data
p2.X = 10;

Console.WriteLine(p1); // (3, 4)  — p1 is unchanged
Console.WriteLine(p2); // (10, 4) — only p2 was modified

구조체는 데이터 컨테이너이므로 할당은 모든 데이터 멤버를 독립적인 새 인스턴스로 복사합니다. 각 복사본은 고유합니다. 하나를 수정해도 다른 수정에는 영향을 주지 않습니다. 이 동작은 할당이 참조만 복사하고 두 변수가 동일한 개체를 공유하는 클래스와 다릅니다. 차이점에 대한 자세한 내용은 값 형식 및 참조 형식을 참조하세요.

구조체 생성자

클래스에서 수행하는 것과 동일한 방식으로 구조체에서 생성자를 정의할 수 있습니다. 구조체에는 사용자 지정 기본값을 설정하는 매개 변수가 없는 생성자가 있을 수 있습니다. "매개 변수가 없는 생성자"라는 용어는 생성자 논리를 사용하여 만든 인스턴스와 new 식을 사용하여 만든 default 인스턴스(모든 필드를 0으로 초기화하는 인스턴스)를 구분합니다.

struct ConnectionSettings
{
    public string Host { get; set; }
    public int Port { get; set; }
    public int MaxRetries { get; set; }

    public ConnectionSettings()
    {
        Host = "localhost";
        Port = 8080;
        MaxRetries = 3;
    }
}

인수 없이 사용할 new 때 매개 변수가 없는 생성자가 실행됩니다. 식은 default 생성자를 무시하고 모든 필드를 기본값(0, null, false)으로 설정합니다. 차이점에 유의하세요.

var custom = new ConnectionSettings();
Console.WriteLine($"{custom.Host}:{custom.Port} (retries: {custom.MaxRetries})");
// localhost:8080 (retries: 3)

var defaults = default(ConnectionSettings);
Console.WriteLine($"{defaults.Host ?? "(null)"}:{defaults.Port} (retries: {defaults.MaxRetries})");
// (null):0 (retries: 0)

컴파일러는 생성자에서 명시적으로 설정하지 않은 모든 필드를 자동으로 초기화합니다. 기본값이 아닌 값이 필요한 필드만 초기화할 수 있습니다.

struct GameTile
{
    public int Row { get; set; }
    public int Column { get; set; }
    public bool IsBlocked { get; set; }

    public GameTile(int row, int column)
    {
        Row = row;
        Column = column;
        // IsBlocked is automatically initialized to false
    }
}
var tile = new GameTile(2, 5);
Console.WriteLine($"Tile ({tile.Row}, {tile.Column}), blocked: {tile.IsBlocked}");
// Tile (2, 5), blocked: False

생성자에서 IsBlocked 속성이 할당되지 않으므로, 컴파일러가 이를 bool의 기본값인 false으로 설정합니다. 이 기능은 몇 개의 필드만 설정해야 하는 생성자의 상용구 수를 줄입니다.

읽기 전용 구조체 및 읽기 전용 멤버

A readonly struct 는 구조체의 상태를 수정하는 인스턴스 멤버가 없음을 보장합니다. 컴파일러는 모든 필드 및 자동 구현 속성을 읽기 전용으로 요구하여 이 보증을 적용합니다.

readonly struct Temperature
{
    public double Celsius { get; }

    public Temperature(double celsius) => Celsius = celsius;

    public double Fahrenheit => Celsius * 9.0 / 5.0 + 32.0;

    public override string ToString() => $"{Celsius:F1}°C ({Fahrenheit:F1}°F)";
}
var temp = new Temperature(100);
Console.WriteLine(temp); // 100.0°C (212.0°F)
// temp.Celsius = 50; // Error: property is read-only

전체 구조체를 변경할 필요가 없는 경우 개별 멤버를 readonly 대신 표시합니다. 멤버는 readonly 구조체의 상태를 수정할 수 없으며 컴파일러는 다음을 보장합니다.

struct Velocity
{
    public double X
    {
        readonly get;
        set;
    }

    public double Y
    {
        readonly get;
        set;
    }

    public readonly double Speed => Math.Sqrt(X * X + Y * Y);

    public readonly override string ToString() => $"({X}, {Y}) speed={Speed:F2}";
}
var v = new Velocity { X = 3, Y = 4 };
Console.WriteLine(v.Speed); // 5
Console.WriteLine(v);       // (3, 4) speed=5.00
v.X = 6;
Console.WriteLine(v.Speed); // 7.211...

멤버 readonly 를 표시하면 컴파일러에서 방어 복사본을 최적화할 수 있습니다. 매개 변수를 readonly 허용하는 in 메서드에 구조체를 전달하는 경우 컴파일러는 복사본이 필요하지 않음을 알 수 있습니다.

구조체를 사용하는 경우

형식이 다음과 같은 경우 구조체를 사용합니다.

  • 단일 값 또는 관련된 값의 작은 그룹(약 16바이트 이하)을 나타냅니다.
  • 값 의미 체계가 있습니다. 데이터가 같은 두 인스턴스는 같아야 합니다.
  • 주로 동작 모델이 아닌 데이터 컨테이너입니다.
  • 기본 형식에서 상속이 필요하지 않습니다(구조체는 다른 구조체 또는 클래스에서 상속할 수 없지만 인터페이스를 구현할 수 있습니다).

클래스, 레코드, 튜플 및 인터페이스를 포함하는 보다 광범위한 비교는 형식 선택을 참조하세요.

참고하십시오