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 |