언어 자료구조 알고리즘/Escort C#

[C#] 3. 형식 개요, 3.1 object

언제나휴일 2016. 4. 1. 16:48
반응형

 C#은 강력한 형식의 언어로 모든 변수와 상수 및 메서드 시그니처의 입력 매개변수와 반환 값 등의 형식을 지정해야 합니다.

 

 형식은 분류 방법에 따라 기본 형식과 사용자 정의 형식으로 나눌 수도 있으며 값 형식, 참조 형식, 포인터 형식으로 구분할 수도 있습니다.

 

 기본 형식에는 true false를 값으로 가질 수 있는 bool 형식과 유니코드 문자를 표현하기 위한 char 형식, 정수와 실수를 표현하기 위한 여러 가지 형식과 문자열을 위해 string과 모든 형식의 기반 형식인 object를 제공하고 있습니다. 기본 형식으로 제공되는 형식 중에 string object는 참조 형식이며 나머지 형식들은 모두 value 형식입니다.

 

C# 형식

.NET Framework형식

설명 및 표현 범위

bool

System.Boolean

논리 값을 표현 (true 혹은 false)

byte

System.Byte

부호 있는 8비트 정수(-128~127)

sbyte

System.SByte

부호 없는 8비트 정수(0~255)

char

System.Char

유니코드 16비트 문자

decimal

System.Decimal

128비트 데이터 형식

double

System.Double

15~16개의 자릿수를 갖는 실수 (근사치)

float

System.Single

7개의 자릿수를 갖는 실수(근사치)

int

System.Int32

부호 있는 32비트 정수

uint

System.UInt32

부호 없는 32비트 정수

long

System.Int64

부호 있는 64비트 정수

ulong

System.UInt64

부호 없는 64비트 정수

object

System.Object

모든 형식의 기반 형식

short

System.Int16

부호 있는 16비트 정수

ushort

System.UInt16

부호 없는 16비트 정수

string

System.String

0개 이상의 유니코드 문자로 구성된 시퀀스

 

 C#에서는 기본 형식외에 필요한 형식을 정의하여 사용할 수 있습니다. 사용자 정의 형식은 표현할 수 있는 값의 종류를 정의하는 열거형과 여러가지 멤버를 캡슐화할 수 있는 구조체와 클래스 등이 있습니다.

 

 C#의 형식은 값 형식과 참조 형식으로 나눌 수 있는데 구조체와 열거형은 값 형식이고 클래스는 참조 형식입니다. C#에서 값 형식들은 변수들이 독립적으로 사용됩니다. 이에 반해 참조 형식은 변수들이 관리화 힙에 할당된 개체를 참조하여 사용되므로 하나의 개체를 사용하는 여러 개의 변수가 있을 수 있습니다. 그리고 참조 형식의 개체는 관리화 힙에 할당되어 .NET에 의해 생명 주기를 관리되어 형식 안정성을 제공합니다.

 

3.1 object

 

 C# object .NET Framework Object 형식을 부르는 형식 이름입니다. 앞에서도 계속 얘기를 했던 것처럼 object 형식은 모든 형식의 기반이 되는 형식입니다. C#에서 사용자 정의 형식을 정의하면 묵시적으로 object 형식에서 파생됩니다. 또한, 개발자는 파생 관계를 문법적으로 표현할 필요도 없고 표현해서도 안 됩니다.

 

 먼저, object 형식을 구성하는 멤버들을 살펴봅시다.

 

 object 형식에서 생성자는 기본 생성자를 제공하고 있습니다. 기본 생성자란 입력 매개 변수가 없는 생성자를 말합니다.

 

public object()

 

 object 형식에서 접근 지정이 public으로 지정된 멤버 메서드는 다음과 같습니다.

 

object 형식의 public으로 접근 지정된 멤버 메서드

public virtual bool Equals(object obj)

public static bool Equals (object obj1, object obj2)

public virtual int GetHashCode()

public Type GetType()

public static bool ReferenceEquals (object obj1,object obj2)

public virtual string ToString ()

 

 C#에서 static 키워드가 명시된 멤버를 정적 멤버라고 합니다. 정적 멤버는 형식 명을 통해 접근이 가능한 멤버로 개체의 멤버가 아니라 형식의 멤버입니다. 그리고 virtual 키워드가 있는 메서드는 가상 메서드로 파생 형식에서 재정의할 수 있습니다. 재정의란 기반 형식에서 정의한 것을 무효화시키고 새롭게 정의하는 것을 말합니다. 이에 대한 자세한 사항은 6장 캡슐화와 7장 상속과 다형성에서 자세히 설명하겠습니다.

 

3.1.1 Equals, ReferenceEquals

 

 Equals 메서드와 ReferenceEquals 메서드는 두 개의 개체가 같은지를 비교할 때 사용하는 메서드이며 정적 멤버와 개체의 멤버가 있습니다. 정적 멤버는 입력 매개변수로 전달된 두 개의 인자가 같은지를 판단하며 개체의 멤버는 입력 매개변수로 전달된 인자와 자신이 같은지를 판단합니다. 기본적으로는 참조된 개체가 같은지를 판단하지만 개체의 멤버 Equals는 가상 메서드이기 때문에 목적에 따라 값이 같은지를 판단하게 재정의가 가능합니다. 그리고 값 형식과 string 형식에서는 개체의 멤버 Equals를 값이 같은지를 판단하도록 재정의되어 있습니다. ReferenceEquals는 참조된 개체가 같은지를 판단합니다.

 

▶ 값 형식의 Equals, ReferenceEquals 메서드

static void Main(string[] args)

{

    int i = 2;

    int j = i;

    Console.WriteLine("i.Equals(j) => {0}", i.Equals(j));

    Console.WriteLine("object.Equals(i,j) => {0}",object.Equals(i, j));

    Console.WriteLine("object.ReferenceEquals(i,j) =>{0}", object.ReferenceEquals(i, j));

}

▶ 결과

i.Equala(j) => True

object.Equals(i,j) => True

object.ReferenceEquals(i,j) => False

 

 위 같은 경우에 i j는 값이 같습니다. 값 형식은 개체의 멤버 Equals를 값이 같은지를 판단하도록 재정의가 되어 있어 i.Equals(j) 결과는 참이 됩니다. 정적 멤버 Equals는 내부적으로 입력 인자로 전달된 첫 번째 개체의 멤버 Equals 메서드를 호출하여 결과를 반환하므로 object.Equals(i,j) 결과도 참이 됩니다.정적 멤버 ReferenceEquals는 개체가 같은지를 판별하는데 값 형식은 변수가 독립적으로 할당되어 관리되므로 i j는 같을 수 없습니다. 이에 object.RefernceEquals(i,j) 결과는 거짓입니다.

 

 

 

 string은 참조 형식이지만 값 형식처럼 개체의 멤버 Equals를 값이 같은지를 판단하도록 재정의되어 있어 값을 비교합니다. 하지만 서로 다른 개체일 경우 ReferenceEquals의 결과는 거짓이 됩니다.

 

string 형식의 Equals ReferenceEquals

static void Main(string[] args)

{

    string s1 = "hello";

    string s2 = s1;

    string s3 = string.Format("hello");

 

    Console.WriteLine("s1.Equals(s2) => {0}", s1.Equals(s2));

    Console.WriteLine("s1.Equals(s3) => {0}", s1.Equals(s3));

    Console.WriteLine("object.ReferenceEquals(s1,s2) =>{0}",

        object.ReferenceEquals(s1, s2));

    Console.WriteLine("object.ReferenceEquals(s1,s3) =>{0}",

        object.ReferenceEquals(s1, s3));

 }

▶ 결과

s1.Equals(s2) => True

s2.Equals(s3) => True

object.ReferenceEquals(s1,s2) => True

object.ReferenceEquals(s1,s3) => False

 

 위의 예에서 s1 s2는 같은 개체를 참조합니다. 그리고 s3 string.Format 메서드를 이용하여 새로운 개체를 생성하였습니다. s1 s2 Eqauls ReferenceEquals의 결과가 모두 참으로 나오지만 s1 s3 ReferenceEquals은 거짓이 나오는 것을 확인할 수 있습니다. 하지만 string Equals 메서드는 값이 같은지를 비교하여 결과를 반환하므로 s1 s3 Equals 결과는 참입니다.

 

 C#에서는 string을 제외한 참조 형식의 Equals 메서드는 참조하는 개체가 같은지를 판단한다는 것에 주의하세요. 물론, 개발자가 재정의하여 목적에 맞게 변경할 수도 있습니다.

 

▶ 참조 형식의 Equals ReferenceEquals

class Example

{

    int val;

    public Example(int val)

    {

        this.val = val;

    }

}

class Program

{

    static void Main(string[] args)

    {

        Example e1 = new Example(1);

        Example e2 = new Example(1);

        Console.WriteLine("e1.Equals(e2) => {0}", e1.Equals(e2));

        Console.WriteLine("object.Equals(e1,e2) => {0}", object.Equals(e1, e2));

        Console.WriteLine("object.ReferenceEquals(e1,e2) =>{0}",

            object.ReferenceEquals(e1, e2));

        Example e3 = e1;

        Console.WriteLine("e1.Equals(e3) => {0}", e1.Equals(e3));

        Console.WriteLine("object.Equals(e1,e3) => {0}", object.Equals(e1, e3));

        Console.WriteLine("object.ReferenceEquals(e1,e3) =>{0}",

            object.ReferenceEquals(e1, e3));

    }

}

▶ 결과

e1.Equals(e2) => False

object.Equals(e1,e2) => False

object.ReferenceEquals(e1,e2) =>False

e1.Equals(e3) => True

object.Equals(e1,e3) => True

object.ReferenceEquals(e1,e3) =>True

 

 테스트를 위해 Example 클래스를 정의하였습니다. 그리고 테스트는 두 개의 변수가 같은 값을 갖는 다른 개체를 각각 참조할 때와 두 개의 변수가 같은 개체를 참조할 때에 대하여 테스트를 하였습니다. 참조 형식의 Equals 메서드는 같은 개체인지 비교하므로 세 가지 모두 결과가 같습니다. 두 개의 변수가 같은 값을 갖는 다른 개체를 각각 참조하는 경우에는 참조한 개체가 다르므로 값이 같아도 결과는 모두 거짓입니다. 그리고 두 개의 변수가 같은 개체를 참조할 때의 결과는 모두 참입니다.

 

 이번에는 class Example struct Example로 변경하여 값 형식일 경우에 어떻게 되는지에 대해 얘기를 해 봅시다. 값 형식은 개체의 멤버 Equals가 값이 같으면 참을 반환므로 서로 다른 개체일 때도 결과는 참입니다. 더군다나 값 형식은 변수마다 독립적으로 메모리가 할당되고 관리되므로 ReferenceEquals의 결과는 모두 거짓입니다.

 

▶ 예제 코드의 Example 클래스를 Example 구조체로 변경했을 때 결과

e1.Equals(e2) => True

object.Equals(e1,e2) => True

object.ReferenceEquals(e1,e2) =>False

e1.Equals(e3) => True

object.Equals(e1,e3) => True

object.ReferenceEquals(e1,e3) =>False

 

3.1.2 GetHashCode

 

 GetHashCode는 해시 테이블과 같은 자료구조를 이용하여 개체를 관리할 때 적합한 키를 반환하는 메서드입니다. 실제 반환되는 값이 어떻게 발생하는지 규칙성이 없고 유일성을 보장하지 않습니다. 확률적으로 같은 값을 반환할 경우가 적다는 이유로 고유한 키로 사용하지 마십시오. 물론, GetHashCode는 가상 메서드이기 때문에 목적에 맞게 재정의할 수 있습니다.

 

3.1.3 GetType

 

 GetType 메서드는 형식에 대한 상세정보를 갖는 Type 개체를 반환합니다. Type 개체는 런 타임에 형식에 대한 상세정보가 필요할 때 사용되는데 여러분들은 이 책을 보시고 난 후에 동적으로 라이브러리를 사용하는 리플렉션에 대해 살펴보세요.

 

 

3.1.4 ToString

 

 ToString 메서드는 자신에 대한 정보를 문자열로 반환하는 메서드입니다. 기본적으로 형식 이름을 반환합니다. 그리고 ToString은 재정의가 가능한 메서드이며 기본 형식들은 갖고 있는 값을 문자열로 변환하여 반환하고 있습니다. 여러분이 사용자 정의 형식을 만들 때 이를 재정의하여 주요한 키를 문자열로 변환하여 반환하면 많은 곳에서 쉽게 사용할 수 있습니다.

 

ToString 메서드 재정의

namespace Example

{

    class Stu//Stu 클래스에는 ToString 메서드를 재정의하였음

    {

        string name;

        public Stu(string name)

        {

            this.name = name;

        }

        public override string ToString()//재정의

        {

            return name;

        }

    }

 

    class Book//Book 클래스에는 ToString 메서드를 재정의하지 않았음

    {

        string name;

        public Book(string name)

        {

            this.name = name;

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            //Stu 클래스에는 ToString 메서드를 중복 정의하였음

            Stu stu = new Stu("홍길동");

            Console.WriteLine(stu.ToString());//명시적으로 ToString 메서드 사용

            Console.WriteLine(stu);

 

            //Book 클래스에는 ToString 메서드를 중복 정의하지 않았음

            Book book = new Book("IT 전문가로 가는 길 Escort C#");

            Console.WriteLine(book.ToString());//명시적으로 ToString 메서드 사용

            Console.WriteLine(book);

        }

    }

}

▶ 결과

홍길동

홍길동

Example.Book

Example.Book

 

반응형