갓똥
나는야 프로그래머
갓똥
전체 방문자
오늘
어제
  • 분류 전체보기 (186)
    • 프로그래밍 (146)
      • 자바 (9)
      • 안드로이드 (2)
      • 유니티 (20)
      • C++ (38)
      • C# (56)
      • HTML (2)
      • 파이썬 (3)
      • 자료구조 (2)
      • 알고리즘 (0)
      • 문제풀이 (4)
      • 디자인 패턴 (7)
      • 카카오톡 봇 (1)
      • 엑셀 (1)
      • 기타 (1)
    • 게임 (21)
      • 테일즈위버 (0)
      • 카이로소프트 (1)
      • 순위 (19)
      • 기타 (1)
    • 일상 (13)
      • 카페 (1)
      • 방탈출 (12)
    • 기타 (6)
      • 웃긴자료 (5)

블로그 메뉴

  • 홈
  • 방명록

공지사항

인기 글

태그

  • C++ 소멸자
  • C++
  • C++ virtual
  • c# coroutine
  • 글로벌게임매출
  • 롤 골드그래프
  • 유니티 골드그래프
  • c# Thread
  • C# 예외 처리
  • c# unboxing
  • 2020년 게임 매출
  • C++ 상속
  • 모바일 게임 순위
  • c# delegate
  • 자바
  • pc게임 순위
  • 전세계게임매출순위
  • C# boxing
  • c# 코루틴
  • 게임 디자인 패턴
  • c# collection
  • Unity Graph
  • 전세계 게임 매출
  • 게임매출순위
  • 유니티 그래프 그리기
  • 알고리즘
  • pc 게임 순위
  • 게임 매출 순위
  • 유니티 그래프
  • 강남 방탈출

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
갓똥

나는야 프로그래머

[C#] Boxing / Unboxing (2)
프로그래밍/C#

[C#] Boxing / Unboxing (2)

2020. 2. 11. 17:31
728x90
반응형

1. 핵심 정리

using System;

class Program {
    static void Main() {
        Console.WriteLine(10 < 20);  // true
        
        Console.WriteLine(10.CompareTo(20));  // -1
        Console.WriteLine(10.CompareTo(10));  // 0
        Console.WriteLine(10.CompareTo(5));   // 1
        
        string s1 = "AAA";
        string s2 = "BBB";
        
        Console.WriteLine(s1 < s2);
        
        Console.WriteLine(s1.CompareTo(s2));  // -1
    }
}

 ① 객체의 크기를 비교하는 방법

C#에서 객체의 크기를 비교하는 방법은 관계연산자와 CompareTo메소드가 있다.
관계연산자는 bool타입의 true, false를 반환하고
CompareTo는 int타입으로 앞의 객체가 작다면 -1, 같다면 0, 크다면 1을 반환한다.
위의 값타입들은 문제가 없다. 이번엔 string을 한 번 보자.
14번째라인에서 string으로 관계연산자를 이용한 비교를 해보고 있다.
근데 문제는 이러면 에러가 나는 것을 볼 수 있다. string은 관계연산자를 쓸 수 없다.
string은 비교를 하려면 CompareTo메소드를 사용해야 한다.
비교 자체는 ASCII코드를 이용하여 한다.

 


2. CompareTo() #1

using System;

class Point {
    private int x;
    private int y;
    public Point(int xPos, int yPos) {
        x = xPos;
        y = yPos;
    }
}

class Program {
    static void Main() {
        Point p1 = new Point(1, 1);
        Point p2 = new Point(2, 2);
        
        Console.WriteLine(p1.CompareTo(p2));
    }
}

 

Point 클래스를 만들고 Main에서 두 개의 객체를 만든 후 크기비교를 하는 코드이다.
관계연산자로 제공해도 되지만 C#에서 일반적인건 CompareTo이므로 CompareTo를 만들어보자.
CompareTo의 동작방식은 아래와 같다.

 ① A.CompareTo(B)

using System;

class Point {
    private int x;
    private int y;
    public Point(int xPos, int yPos) {
        x = xPos;
        y = yPos;
    }
    public int CompareTo(Point other) {
        if(x > other.x) return 1;
        else if(x == other.x) return 0;
        return -1;
    }
}

class Program {
    static void Main() {
        Point p1 = new Point(1, 1);
        Point p2 = new Point(2, 2);
        
        Console.WriteLine(p1.CompareTo(p2));
    }
}

 

위와 같이 만들어 사용할 수 있다. 간단하게 x만을 비교하고 있는데 비교정책은 맘대로 바꾸면 된다.
이제 Point로 만든 객체도 CompareTo를 사용할 수 있게 되었다.
그렇다면 이런 가정을 한 번 해보자.
C#의 대부분의 class들이 CompareTo를 사용하는데 실수로 compareTo로 만들었다고 해보자.
사실 이렇게 만들어도 쓸 때 compareTo로 사용하면 되지만 일관성이 없다.
그렇다면 CompareTo를 만들 땐 모두 같은 이름으로 쓰자 라고 약속하면 좋을텐데
이러한 약속이 객체지향의 기본 문법에서 함수 이름을 약속하는 문법은 interface가 있다.
그렇다면 아래와 같이 바꿀 수 있다.
using System;

// 비교하는 메소드의 이름을 약속하자.
interface IComparable {
    int CompareTo(Point other);
}

// 모든 비교 가능한 객체는 IComparable 인터페이스를 구현해야 한다.
class Point : IComparable {
    private int x;
    private int y;
    public Point(int xPos, int yPos) {
        x = xPos;
        y = yPos;
    }
    public int CompareTo(Point other) {
        if(x > other.x) return 1;
        else if(x == other.x) return 0;
        return -1;
    }
}

class Program {
    static void Main() {
        Point p1 = new Point(1, 1);
        Point p2 = new Point(2, 2);
        
        Console.WriteLine(p1.CompareTo(p2));
    }
}

 

이제 모든 비교 가능한 객체를 만들 땐 IComparable 인터페이스를 상속하여
CompareTo에 대한 이름의 일관성을 지켰다.
근데 문제가 있다. 사실 IComparable을 만든 의미대로 Point만 쓸게 아닌 다른 모든 객체에서 쓸 수 있어야 한다.
Point를 object로 바꿈으로 해결할 수 있다. 아래에서 해결법을 보자.
using System;

interface IComparable {
    int CompareTo(object other);
}

class Point : IComparable {
    private int x;
    private int y;
    public Point(int xPos, int yPos) {
        x = xPos;
        y = yPos;
    }
    public int CompareTo(object other) {
        Point pt = other as Point;
        if(x > pt.x) return 1;
        else if(x == pt.x) return 0;
        return -1;
    }
}

class Program {
    static void Main() {
        Point p1 = new Point(1, 1);
        Point p2 = new Point(2, 2);
        
        Console.WriteLine(p1.CompareTo(p2));
    }
}

 

마지막으로 IComparable은 사실 우리가 만드는게 아닌 이미 C#에 있다.
따라서 지워도 아무문제 없다.
정의로 이동해보면 int CompareTo(object obj)가 있는걸 볼 수 있다.

 ② IComparable 인터페이스

    => CompareTo 메소드에 대한 규칙을 담은 인터페이스

    => 크기 비교가 가능한 타입은 IComparable 인터페이스를 구현해야 한다.

    => C#은 대부분의 메소드의 규칙을 인터페이스로 제공하고 있다.

 

다음으로 위의 코드의 문제점을 살펴보고 개선해보자.

3. CompareTo() #2

using System;

class Point : IComparable {
    private int x;
    private int y;
    public Point(int xPos, int yPos) {
        x = xPos;
        y = yPos;
    }
    public int CompareTo(object other) {
        Point pt = other as Point;
        
        if(x > pt.x) return 1;
        else if(x == pt.x) return 0;
        return -1;
    }
}

class Program {
    static void Main() {
        Point p1 = new Point(1, 1);
        Point p2 = new Point(2, 2);
        
        Console.WriteLine(p1.CompareTo(p2));
    }
}

 

자 이제 문제점을 살펴보자.
위의 포인트는 class로 만들었다. 하지만 포인트가 struct로 만들었다고 가정해보자.
그럼 아래와 같이 코드를 수정해서 문제없이 사용할 수 있다.
using System;

struct Point : IComparable {
    private int x;
    private int y;
    public Point(int xPos, int yPos) {
        x = xPos;
        y = yPos;
    }
    public int CompareTo(object other) {
        // Point pt = other as Point;
        Point pt = (Point)other;
        
        if(x > pt.x) return 1;
        else if(x == pt.x) return 0;
        return -1;
    }
}

class Program {
    static void Main() {
        Point p1 = new Point(1, 1);
        Point p2 = new Point(2, 2);
        
        Console.WriteLine(p1.CompareTo(p2));
    }
}

 

casting만 바꾸면 struct로 바꾸어도 아무 문제가 없다.
하지만 Main에서 Point는 현재 값 타입이다. 근데 CompareTo에서 인자로 받는건 object로 받고 있다.
object는 레퍼런스 타입이므로 인자가 넘어가며 Boxing이 발생한다. 
또 CompareTo 내부에서 캐스팅할 때 Unboxing이 발생한다.
이는 인자로 넘어갈 때, 캐스팅 될 때 모두 복사본이 만들어지므로 성능저하가 발생할 수 있다.
해결책은 object가 아닌 Point로 받으면 되지만 그러면 interface를 쓸 수 없다.
using System;

// C# 1.0
// interface IComparable {
//     int CompareTo(object other);
// }

// C# 2.0 : generic interface
interface IComparable<T> {
    int CompareTo(T other);
}

struct Point : IComparable<Point> {
    private int x;
    private int y;
    public Point(int xPos, int yPos) {
        x = xPos;
        y = yPos;
    }
    public int CompareTo(Point pt) {        
        if(x > pt.x) return 1;
        else if(x == pt.x) return 0;
        return -1;
    }
}

class Program {
    static void Main() {
        Point p1 = new Point(1, 1);
        Point p2 = new Point(2, 2);
        
        Console.WriteLine(p1.CompareTo(p2));
    }
}

 

C#초기에는 generic이 없었다.
그 후 C#이 발전되며 2.0에서 generic이 생겨났다.
따라서 위와 같이 수정할 수 있으며 더이상 Boxing, Unboxing이 발생하지 않게 되었다.
위와 마찬가지로 IComparable<T>는 이미 구현되어 있어 지워도 문제 없다.

 ① IComparable vs IComparable<T>

마지막으로 작성한 코드도 완벽한 해결책은 아니다.
만약 Main에서 객체를 생성할 때
Point p2 = new Point(2, 2); 가 아닌
object p2 = new Point(2, 2); 로 만들었다고 한다면
CompareTo(p2)에서 인자를 보낼 수가 없다.
따라서 최고의 해결책은 두 가지를 모두 만드는 것이다.
using System;

struct Point : IComparable<Point>, IComparable {
    private int x;
    private int y;
    public Point(int xPos, int yPos) {
        x = xPos;
        y = yPos;
    }
    public int CompareTo(Point pt) {        
        if(x > pt.x) return 1;
        else if(x == pt.x) return 0;
        return -1;
    }
    public int CompareTo(object other) {
        Point pt = (Point)other;
        
        if(x > pt.x) return 1;
        else if(x == pt.x) return 0;
        return -1;
    }
}

class Program {
    static void Main() {
        Point p1 = new Point(1, 1);
        Point p2 = new Point(2, 2);
        
        Console.WriteLine(p1.CompareTo(p2));
    }
}

 ② 권장 사항

    => IComparable 과 IComparable<T> 인터페이스를 모두 구현하는 것이 좋다.

728x90
반응형

'프로그래밍 > C#' 카테고리의 다른 글

[C#] 배열 기본  (0) 2020.02.14
[C#] Boxing / Unboxing (3)  (1) 2020.02.11
[C#] Boxing / Unboxing (1)  (1) 2020.02.10
[C#] casting  (0) 2020.02.07
[C#] Elvis Operator (?, ??) 연산자  (0) 2020.02.06
    '프로그래밍/C#' 카테고리의 다른 글
    • [C#] 배열 기본
    • [C#] Boxing / Unboxing (3)
    • [C#] Boxing / Unboxing (1)
    • [C#] casting
    갓똥
    갓똥
    공부하며 알아가는 내용을 정리해 봅니다.

    티스토리툴바