값 형식과 참조 형식 이해하기
// 구조체는 값 형식이다.
struct FOO
{
public int x,y;
}
// 기본 생성자를 이용해서 생성할 때는 'new'키워드가 생략 될수 있지만, 필드들은 모두 사용되기 전에 할당되어야 한다.
FOO f1 = new FOO();
f1.x = 100;
f1.y = 100;
FOO f2 = f1;
// f2.x 를 변경해도 f1.x는 변경되지 않는다.
f2.x = 900;
이와 반대로, 참조 형식(클래스)은 가비지 컬렉션의 대상인 힙에 할당된다. 힙에 할당된 참조 형식은
가비지 컬렉터에 의해 제거되기 전까지는 계속 메모리에 남아 있게 된다. 기본적으로, 참조 형식을
할당하면 메모리에 있는 동일한 객체에 대한 새로운 참조자가 만들어 진다.
class FOO
{
public int x, y;
}
이렇게 바꾸어서 테스트해보면 이제 관리 힙에 있는 동일한 객체를 지시하는 두 개의 참조자가
생겨났기 때문이다. 그래서 f2 참조자의 x값을 변경하면 f1.x도 같은 값을 나타내게 된다.
참조 형식을 포함하는 값 형식
InnerRef (내부에 참조 형식을 포함하는 값 형식)
InnerRef 변수 하나를 다른 InnerRef 변수에 할당하면 어떤 일이 벌어 질까?
내부 참조 형식은 새로운 InnerRef 변수로 완전히 복사되지 않는다.
구조체는 서로 다른 두개가 되지만, 참조자들은 메모리에 있는 동일한 객체를 지시하게 된다.
마스터 노드 : System.Object
C#에서는 모든 데이터 형식(값 기반이든 참조 기반이든)은 결국 공용 기본 클래스인 System.Object로
부터 파생한다. Object가 기본 클래스로 명시되어 있지 않은 것을 볼 수 있는데, 다른 기본 클래스를
명시하지 않으면 기본 클래스가 Object 인 것으로 간주된다.
// 객체 참조자 비교...
ObjTest c1 = new ObjTest();
// c1 에 대한 참조자들을 만든다.
ObjTest c2 = c1;
object o = c2;
//세 인스턴스가 모두 메모리상의 동일한 객체를 가리키고 있나?
if(o.Equals(c1) && c2.Equals(o))
Console.WriteLine("Same instance");
Equals()는 기본적으로 값 의미구조가 아닌 참조 의미구조로 두 객체의 변수를 비교하게 되어 있다.
c1, c2 그리고 o 가 모두 메모리상의 동일한 객체를 가리키기 때문에, 같은 비교 결과는 참이다.
System.Object 의 기본 기능 재정의 하기
System.Object에 미리 준비된 기능으로 충분하긴 하지만, 이 동작들을 상속한 메소드를 재정의하는 것이 일반적이다.
파생 클래스에서 이 가상 멤버들을 임의대로 구현하고 싶으면 C#의 'override' 키워드를 사용하면 된다.
System.Object의 정적 멤버들
값 기반 또는 참조 기반으로 동등 비교를 할 수 있는 두 개의 정적 멤버가 정의되어 있다.
//System.Object의 정적 멤버.
Person p3 = new Person("Sally", "Jones", "333", 4);
Person p4 = new Person("Sally", "Jones", "333", 4);
// p3와 p4의 상태가 동일한가? 그러타!
Console.WriteLine("P3 and P4 have same state : {0}", object.Equals(p3, p4));
// 이 둘은 메모리상의 동일한 객체인가 ? 아니다!
Console.WriteLine("P3 and P4 are pointing to same object : {0} ",
object.ReferenceEquals(p3, p4));
.NET 기본 클래스 라이브러리에 정의되어 있는 모든 클래스, 구조체, 열거형 그리고 델리게이트는 항상
System.Object의 public 멤버를 지원한다. 그러나 인터페이스는 System.Object 또는 다른 어떤 기본 클래스로부터
파생되지 않는다!!!
문자열 데이터로부터 값 구문분석하기
.NET 데이터 형식은 이 형식을 표현하는 문자열로부터 본래 형식의 변수를 생성(즉, 구문분석 parsing)할 수 있다.
입력한 데이터를 숫자 값으로 변환하고 싶을 때 특히 유용하게 사용될 수 있다.
double MyDbl = double.Parse("99.884");
int myInt = int.Parse("8");
char myChar = char.Parse("w");
값 형식과 참조 형식 간의 변환 : 박싱(boxing)과 언박싱(unboxing)
.NET 에서는 형식을 크게 두 가지 범주(값 기반과 참조 기반)로 나뉘는데, 경우에 따라 한 범주에 속하는
변수를 다른 범주의 변수로 나타낼 필요가 있ㅇ르 수 있다. C#에는 값 형식과 참조 형식 간의 변환을
간단하게 할 수 있는 메커니즘이 있는데, 이를 박싱(boxing)이라고 부른다.
// 값 데이터 포인트를 만든다.
short s = 25;
// 값을 객체 참조로 박스한다.
object objShort = s;
박싱은 명시적으로 값 형식을 이에 상응하는 참조 형식으로 변환하는 과정이라고 정의할 수 있다.
언박싱은 객체 참조에 들어 있는 ㄱ밧을 이에 상응하는 값 형식으로 변환해 스택에 올려놓는 과정을 일컫는 용어이다.
// 참조를 이에 상응하는 short로 언박스한다.
short anotherShort = (short)objShort;
알맞은 데이터 형식으로 언박스한느 것은 상당히 주의를 요하는 작업이다.