C#프로그래밍 11 : 구조체, 튜플

LeeWonjin·2022년 5월 6일
0

[학부]C#프로그래밍

목록 보기
11/21

구조체

struct키워드를 사용해 구조체를 선언할 수 있다.

메소드에 대한 특징

C의 구조체와 다르게 필드뿐 아니라 메소드도 가질 수 있다.
이 때, 구조체 또한 object클래스를 상속하기 때문에 object의 메소드를 오버라이드할 수 있다.

그 외 특징

  • 선언만으로 생성 가능
  • 값형식 (스택에 메모리공간 할당, 대입연산자로 깊은복사)
  • 매개변수 없는 생성자 선언 불가
  • 상속 불가, 은닉성 가질 필요 없음 (추상화를 위한 도구가 아님)
namespace Program
{
    struct Gorani
    {
        public int age;
        public int id;

        public Gorani(int age, int id)
        {
            this.age = age;
            this.id = id;
        }

        public override string ToString()
        {
            return $"Gorani ID:{id} -- AGE:{age}";
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Gorani a; // new 없이 선언
            a.id = 1;
            a.age = 10;

            Gorani b = new Gorani(456, 2); // new로 선언

            Gorani c = b; // 값형식이므로 깊은복사
            c.id = 3;
            c.age = 789;

            Gorani[] goranis = { a, b, c };
            foreach( Gorani gorani in goranis)
            {
                Console.WriteLine(gorani);
            }

        }
    }
}

변경 불가능 구조체 선언 (readonly)

struct앞에 readonly키워드를 붙여 변경 불가능 구조체로 선언할 수 있다.
이 경우 모든 필드도 readonly로 선언되어야 한다.
(생성자에서만 필드 초기화 가능)

namespace Program
{
    readonly struct Gorani
    {
        public readonly int id;

        public Gorani(int id)
        {
            this.id = id;
        }

        public override string ToString()
        {
            return $"Gorani ID:{id}";
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Gorani a = new Gorani(1); // 생성자 호출 --> 정상컴파일

            Gorani b; // new 없이 선언 
            b.id = 2; //  --> 컴파일에러 (생성자 밖에서 필드 수정시도)
        }
    }
}

읽기 전용 메소드

구조체 내의 메소드에 readonly키워드를 붙여 읽기 전용 메소드로 선언할 수 있다.
이 메소드 내에서는 필드 값을 변경할 수 없다.

namespace Program
{
    struct Gorani
    {
        public readonly int id;
        public int canbeChanged;
        public Gorani(int id, int canbeChanged)
        {
            this.id = id;
            this.canbeChanged = canbeChanged;
        }

        public readonly void IamMethod()
        {
            canbeChanged += 1; // 컴파일에러
            // (readonly 메소드에서 필드값을 바꾸려고해서)
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Gorani a = new Gorani(1, 555);
        }
    }
}

튜플

명명되지 않은 튜플

필드를 담을 수 있는 형식 이름이 없는 구조체
가장 앞 요소부터 Item1, Item2, Item3, ..., ItemN 으로 자동 명명된다.

public static void Main(string[] args)
{
  var a = (123, true, "aa");
  Console.WriteLine(a); // (123, True, aa)
  Console.WriteLine($"{a.Item1} {a.Item2} {a.Item3}"); // 123 True aa
}

명명된 튜플

이름있는 필드를 담을 수 있는 튜플
식별자 : 값

public static void Main(string[] args)
{
  // 이름있는 튜플
  var a = (x: 123, y: "abc");
  Console.WriteLine(a); // (123, abc)
  Console.WriteLine($"x : {a.x}, y : {a.y}"); // x : 123, y : abc
}

튜플 분해

튜플 분해를 통해 변수를 생성하고 초기화 할 수 있다.
_로 특정 필드를 무시할 수 있다. 만약 _에 접근하려고 하면 "컨텍스트에 없다"는 컴파일 오류를 뱉는다.

가장 마지막 부분의 switch식으로 표현된 코드는 튜플의 위치 패턴 매칭이라고 불린다.

위치 패턴 https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/patterns#positional-pattern

public static void Main(string[] args)
{
    var a = (x: false, y: 5.3);

    // 변수 생성+초기화
    var (xx, yy) = a;
    yy = 53.53;
    Console.WriteLine($"xx : {xx}, yy : {yy}"); // xx : False, yy : 53.53

    // _ 로 특정필드 무시 ( _는 없는 식별자)
    var (_, yyy) = a;
    Console.WriteLine($"x값 없다, yyy : {yyy}"); // x값 없다, yyy : 5.3

    // 위치패턴 매칭 (분해의 활용 예)
    var result = a switch
    {
        (bool b, double d) when b == true => "true다",
        (false, double d) when d > 5.0 => "false고 5.0보다 크다",
        (false, _) => "false고 5.0 이하다",
        _ => "디폴트다",
    };
    Console.WriteLine(result); // false고 5.0보다 크다
}

튜플 할당(대입)

필드의 갯수, 형식이 같은 튜플은 서로 대입할 수 있다.

public static void Main(string[] args)
{
    var a = ( x: false, y: 5.3 );
    var b = ( true, 9999.999 );

    Console.WriteLine(a); // (False, 5.3)
    Console.WriteLine(b); // (True, 9999.999)
    Console.WriteLine();

    a = b;
    Console.WriteLine(a); // (True, 9999.999)
    Console.WriteLine();

    b = (xx: false, yy: 123.123); // xx, yy는 사실 쓸모가 없다.
    Console.WriteLine(b); // (False, 123.123)
    Console.WriteLine();
}
profile
노는게 제일 좋습니다.

0개의 댓글