Favicon

Struct type

Peponi11/19/20248m

C#
SyntaxTypestruct

1. Introduction

struct (구조체) 는 객체 및 메서드를 캡슐화 할 수 있는 값 형식이다. struct 키워드로 정의하며, .NET 기본 값 형식 (int, bool...) 등이 구조체로 구성되어있다. 값 형식이므로 할당, 메서드 인수 전달 및 리턴 시 인스턴스가 복사된다.

struct는 인스턴스가 하나의 값을 의미하므로, 내부 데이터를 변경할 수 없는 형식으로 정의하는 것이 좋다. 또한 값 형식이기 때문에, 작은 크기의 읽기용 데이터 구조를 만드는 데 주로 사용한다. (데이터 자체보다 데이터의 거동이 중요한 경우, 참조 형식인 클래스가 더 적합하다)

2. struct 정의

struct는 기본적으로 다음과 같이 정의한다.

public struct SerialCommunicationInformation
{
    public int COMPortNumber;
    public int Baudrate;
    public System.IO.Ports.Parity Parity;
    public int Databits;
    public System.IO.Ports.StopBits Stopbits;
    public System.IO.Ports.Handshake Handshake;
}

struct 일부 또는 전체를 변경할 수 없게 하는 경우 readonly 키워드를 붙여 사용한다.

// struct 일부
 
public struct CartesianCoordinate
{
    public float X { get; set; }
    public readonly float Y { get; }
 
    public float Y2 { get; init; }   // C# 9.0. 위 멤버 Y와 동일한 기능이다. (생성자를 제외한 곳에서 값 수정 불가)
 
    public CartesianCoordinate(float X, float Y)
    {
        this.X = X;
        this.Y = Y;
    }
}
 
// struct 전체
 
public readonly struct CartesianCoordinate
{
    public float X { get; init; }
    public float Y { get; init; }
}

structHeap 상에 존재하는 것을 방지하기 위해, 아래와 같이 ref 키워드를 사용할 수 있다.

public ref struct CartesianCoordinate
{
    public float X { get; init; }
    public ref float Y;  // C# 11. 프로퍼티 구현 시 setter는 할 수 없다.
 
    public CartesianCoordinate(float x, ref float y)
    {
        X = x;
        Y = ref y;
    }
 
    public override string ToString() => $"{X}, {Y}";
}
 
static void Main(string[] args)
{
    float y = 20;
    var coordinate = new CartesianCoordinate(10, ref y);
 
    Console.WriteLine(coordinate.ToString());
}
 
/* output:
10, 20
*/

recordstruct 키워드를 추가하여 값 형식의 record를 생성 수 있다. (C# 10)

public record struct CartesianCoordinate
{
    public float X { get; init; }
    public float Y { get; init; }
}

C# 12 버전부터는 선언 시 record처럼 기본 생성자를 정의할 수 있다.

  • 기본 생성자의 인수에 대해 자동으로 필드를 생성해주지는 않는다.
  • 추가 생성자를 정의하는 경우, 반드시 기본 생성자를 경유해야한다.
public struct CartesianCoordinate(int x, int y)
{
    public int X { get; init; } = x;
    public int Y { get; init; } = y;
 
    public CartesianCoordinate(int x) : this(x, default) { }
}

3. struct 초기화

struct는 다음과 같이 초기화 할 수 있다.

public struct CartesianCoordinate
{
    public float X { get; init; }
    public float Y { get; init; }
 
    public CartesianCoordinate(float X, float Y)
    {
        this.X = X;
        this.Y = Y;
    }
}
 
var coordinate = new CartesianCoordinate() { X = 10, Y = 20 };
var coordinate2 = new CartesianCoordinate(10, 20);

struct의 필드가 접근 가능한 경우, 조건부로 new 연산자 없이 인스턴스 생성이 가능하다.

  • 모든 필드 또는 일부 필드가 접근 가능한 경우, 할당한 필드만 값을 얻어올 수 있다.
public struct CartesianCoordinate
{
    public float X;
    public float Y;
    private float Z;
 
    public override string ToString() => $"{X}, {Y}, {Z}";
}
 
static void Main()
{
    CartesianCoordinate coordinate;
    coordinate.X = 10;
    coordinate.Y = 20;
 
    Console.WriteLine(coordinate.X);  // 10
    Console.WriteLine(coordinate.Y);  // 20
    Console.WriteLine(coordinate);  // CS0165
}
  • 모든 필드가 접근 가능하고 모든 필드를 할당한 경우, struct 객체가 생성된다.
public struct CartesianCoordinate
{
    public float X;
    public float Y;
    public float Z;
 
    public override string ToString() => $"{X}, {Y}, {Z}";
}
 
static void Main()
{
    CartesianCoordinate coordinate;
    coordinate.X = 10;
    coordinate.Y = 20;
    coordinate.Z = 30;
 
    Console.WriteLine(coordinate.X);  // 10
    Console.WriteLine(coordinate.Y);  // 20
    Console.WriteLine(coordinate.Z);  // 30
    Console.WriteLine(coordinate);    // 10, 20, 30
}

C# 10 버전부터는 파라미터가 없는 생성자 및 필드 이니셜라이저를 정의할 수 있다.

public struct CartesianCoordinate
{
    public float X { get; init; } = 10;
    public float Y { get; init; } = 20;
 
    public CartesianCoordinate()
    {
        X = 10;
        Y = 20;
    }
}

C# 10 버전부터 with 식이 도입되어 구조체의 일부를 수정한 인스턴스 복사가 가능하다.

public struct CartesianCoordinate
{
    public float X { get; init; }
    public float Y { get; init; }
}
 
var coordinate = new CartesianCoordinate();
Console.WriteLine($"{coordinate.X}, {coordinate.Y}");   // 0, 0
    
var coordinate1 = coordinate with { X = 1 };
Console.WriteLine($"{coordinate1.X}, {coordinate1.Y}"); // 1, 0

C# 11 버전부터는 초기화되지 않은 필드에 대해 컴파일러가 초기화 코드를 추가해준다.

public struct CartesianCoordinate
{
    public float X { get; init; }
    public float Y { get; init; }
 
    public CartesianCoordinate(float x)
    {
        X = x;
        // Y = default;     컴파일러가 추가
    }
}

WARNING

default 키워드를 이용한 초기화 시, 기본 생성자가 무시되며 각 멤버의 기본값으로 초기화된다.

public struct CartesianCoordinate
{
    public float X { get; init; }
    public float Y { get; init; }
 
    public CartesianCoordinate()
    {
        X = 10;
        Y = 20;
    }
}
 
var coordinate = new CartesianCoordinate();
Console.WriteLine($"{coordinate.X}, {coordinate.Y}");   // 10, 20
 
var coordinate2 = default(CartesianCoordinate);
Console.WriteLine($"{coordinate2.X}, {coordinate2.Y}"); // 0, 0

4. 인라인 배열

C# 12 버전부터 struct인라인 배열을 사용할 수 있다.

인라인 배열은 다음과 같은 특징을 갖고 있다.

[System.Runtime.CompilerServices.InlineArray(5)]
public struct StructArray
{
    private float _value;
}
static void Main(string[] args)
{
    var array = new StructArray();
    array[2] = 10;
    array[4] = 20;
 
    ReadOnlySpan<float> span = array;  // Length 얻기 위해 span 변환
    Console.WriteLine(span.Length);
    
    Console.WriteLine();
 
    foreach (var value in span)
    {
        Console.WriteLine(value);
    }
}
 
/* output:
5
 
0
0
10
0
20
*/

5. 참조 자료