Visual C# 언어 개념
데이터 형식(C# 및 Java)

이 항목에서는 데이터를 표현, 할당 및 가비지 수집하는 방법과 관련된 Java와 C#의 몇 가지 주요 유사점 및 차이점에 대해 설명합니다.

복합 데이터 형식

필드, 메서드 및 이벤트와 결합된 복합 데이터 형식으로서의 클래스 개념은 Java와 C#에서 모두 비슷합니다. 클래스 상속에 대해서는 상속 및 파생 클래스(C# 및 Java) 항목에서 따로 설명합니다. C#에는 상속을 지원하지 않는 스택 할당된 복합 데이터 형식으로서의 구조체라는 개념이 도입되었습니다. 구조체의 다른 측면은 대부분 클래스와 매우 비슷합니다. 구조체를 사용하면 성능을 중요하게 고려해야 할 시나리오와 자주 반복되는 루프에 사용하기 위한 관련 필드 및 메서드를 간편하게 함께 그룹화할 수 있습니다.

C#에서는 클래스의 인스턴스를 가비지 수집하기 전에 호출되는 소멸자 메서드를 만들 수 있습니다. Java의 경우 finalize 메서드를 사용하면 개체를 가비지 수집하기 전에 리소스를 정리하는 코드를 포함할 수 있습니다. C#의 경우 이 기능은 클래스 소멸자가 수행합니다. 소멸자는 인수가 없고 물결 문자(~)가 앞에 나오는 생성자와 같습니다.

기본 제공 데이터 형식

C#에서는 Java에서 사용할 수 있는 모든 데이터 형식을 제공하고, 부호 없는 숫자와 새로운 128비트 고정밀도 부동 소수점 형식을 추가로 지원합니다.

Java의 기본 데이터 형식 각각에 대해 핵심 클래스 라이브러리에서는 이를 Java 개체로 나타내는 래퍼 클래스를 제공합니다. 예를 들어, Int32 클래스는 int 데이터 형식을 래핑하고 Double 클래스는 double 데이터 형식을 래핑합니다.

반면, C#의 모든 기본 데이터 형식은 System 네임스페이스의 개체입니다. 각 데이터 형식에 대해 별칭이라고도 하는 약식 이름이 제공됩니다. 예를 들어, intSystem.Int32의 약식 이름이고 doubleSystem.Double의 약식 이름입니다.

다음 표에는 C# 데이터 형식과 해당 별칭의 목록이 나와 있습니다. 표에서 처음 8개 형식은 Java에서 사용할 수 있는 기본 형식에 해당합니다. 그러나 Java의 boolean은 C#에서 bool이라는 점에 주의해야 합니다.

약식 이름 .NET 클래스 형식 너비 범위(비트)

byte

Byte

부호 없는 정수

8

0 ~ 255

sbyte

SByte

부호 있는 정수

8

-128 ~ 127

int

Int32

부호 있는 정수

32

-2,147,483,648 ~ 2,147,483,647

uint

UInt32

부호 없는 정수

32

0 ~ 4294967295

short

Int16

부호 있는 정수

16

-32,768 ~ 32,767

ushort

UInt16

부호 없는 정수

16

0 ~ 65535

long

Int64

부호 있는 정수

64

-922337203685477508 ~ 922337203685477507

ulong

UInt64

부호 없는 정수

64

0 ~ 18446744073709551615

float

Single

단정밀도 부동 소수점 형식

32

-3.402823e38 ~ 3.402823e38

double

Double

배정밀도 부동 소수점 형식

64

-1.79769313486232e308 ~ 1.79769313486232e308

char

Char

단일 유니코드 문자

16

텍스트에 사용되는 유니코드 기호

bool

Boolean

부울 논리 형식

8

true 또는 false

object

Object

다른 모든 형식의 기본 형식

string

String

문자 시퀀스

decimal

Decimal

29개의 유효 자릿수로 10진수를 나타낼 수 있는 정밀 소수 또는 정수 계열 형식

128

±1.0 × 10e−28 ~ ±7.9 × 10e28

C#에서는 모든 기본 데이터 형식을 개체로 나타내므로 기본 데이터 형식에 대해 개체 메서드를 호출할 수 있습니다. 예를 들면 다음과 같습니다.

C#
static void Main()
{
    int i = 10;
    object o = i;
    System.Console.WriteLine(o.ToString());
}    

여기에는 자동 boxing 및 unboxing이 사용됩니다. 자세한 내용은 Boxing 및 Unboxing(C# 프로그래밍 가이드)을 참조하십시오.

상수

Java와 C# 모두 해당 값이 컴파일 타임에 지정되고 런타임에는 이를 변경할 수 없는 변수를 선언할 수 있습니다. Java에서는 그와 같은 변수를 선언하기 위해 final 필드 한정자를 사용하고, C#에서는 const 키워드를 사용합니다. const 이외에도 C#에서는 readonly 키워드를 제공합니다. 이 키워드를 사용하여 선언한 변수는 런타임에 선언 문이나 생성자에서 해당 값을 한 번 할당할 수 있습니다. 초기화한 후에는 readonly 변수의 값을 변경할 수 없습니다. readonly 변수는 개별적으로 컴파일한 모듈에서 버전 번호 같은 데이터를 공유해야 하는 경우에 유용하게 사용할 수 있습니다. 모듈 A를 업데이트하고 새 버전 번호로 다시 컴파일한 경우 모듈 B를 다시 컴파일하지 않고도 새 상수 값을 사용하여 이를 초기화할 수 있습니다.

열거형

열거형은 C와 C++에서 사용할 때와 비슷하게 명명된 상수를 그룹화하는 데 사용됩니다. Java에는 열거형이 사용되지 않습니다. 다음 예제에서는 간단한 Color 열거형을 정의합니다.

C#
public enum Color
{
    Green,   //defaults to 0
    Orange,  //defaults to 1
    Red,     //defaults to 2
    Blue     //defaults to 3
}  

다음 열거형 선언에서와 같이 정수 계열 값을 열거형에 할당할 수도 있습니다.

C#
public enum Color2
{
    Green = 10,
    Orange = 20,
    Red = 30,
    Blue = 40
}

다음 코드 예제에서는 Enum 형식의 GetNames 메서드를 호출하여 열거형에 사용할 수 있는 상수를 표시합니다. 그런 다음 열거형에 값을 할당하고 이 값을 표시합니다.

C#
class TestEnums
{
    static void Main()
    {
        System.Console.WriteLine("Possible color choices: ");

        //Enum.GetNames returns a string array of named constants for the enum.
        foreach(string s in System.Enum.GetNames(typeof(Color)))
        {
            System.Console.WriteLine(s);
        }

        Color favorite = Color.Blue;

        System.Console.WriteLine("Favorite Color is {0}", favorite);
        System.Console.WriteLine("Favorite Color value is {0}", (int) favorite);
    }
}

출력

Possible color choices:

Green

Orange

Red

Blue

Favorite Color is Blue

Favorite Color value is 3

문자열

문자열 형식은 Java와 C#에서 모두 비슷하게 동작하지만 약간의 차이는 있습니다. 두 문자열 형식은 모두 변경할 수 없습니다. 즉, 문자열을 만든 후에는 문자열의 값을 변경할 수 없습니다. 두 언어 모두 문자열의 실제 내용을 수정하는 것처럼 보이는 메서드는 실제로는 원래 문자열을 변경하지 않은 채로 두고 반환할 새 문자열을 만듭니다. C#과 Java에서 문자열 값을 비교하는 프로세스는 서로 다릅니다. Java에서 문자열 값을 비교하려면 개발자가 문자열 형식에 대한 equals 메서드를 호출해야 합니다. == 연산자는 기본적으로 참조 형식을 비교하기 때문입니다. C#에서는 개발자가 == 또는 != 연산자를 사용하여 문자열 값을 직접 비교할 수 있습니다. C#의 경우 문자열이 참조 형식이어도 ==!= 연산자는 기본적으로 참조 대신 문자열 값을 비교합니다.

Java에서와 마찬가지로 C# 개발자는 문자열을 연결하기 위해 문자열 형식을 사용하지 말아야 합니다. 문자열을 연결하는 데 문자열 형식을 사용하면 문자열을 연결할 때마다 새 문자열 클래스를 만들어야 하므로 오버헤드가 발생할 수 있습니다. 개발자는 Java StringBuffer 클래스와 동일한 기능을 수행하는 StringBuilder 클래스를 대신 사용할 수 있습니다.

문자열 리터럴

C#의 경우 문자열 상수 내에서 백슬래시 문자를 나타내는 "\" 또는 탭 문자를 나타내는 "\t" 같은 이스케이프 시퀀스를 사용할 필요가 없습니다. 이렇게 하려면 문자열 값을 할당할 때 그 앞에 @ 기호를 사용하여 약어 문자열을 선언하기만 하면 됩니다. 다음 예제에서는 이스케이프 문자를 사용하는 방법과 문자열 리터럴을 할당하는 방법을 보여 줍니다.

C#
static void Main()
{
    //Using escaped characters:
    string path1 = "\\\\FileShare\\Directory\\file.txt";
    System.Console.WriteLine(path1);

    //Using String Literals:
    string path2 = @"\\FileShare\Directory\file.txt";
    System.Console.WriteLine(path2);
}

변환 및 캐스팅

데이터 형식을 자동으로 변환하고 캐스팅하기 위한 규칙은 Java와 C# 모두 비슷합니다.

Java와 마찬가지로 C#에서는 암시적 형식 변환과 명시적 형식 변환을 모두 지원합니다. 확대 변환의 경우 변환은 암시적입니다. 예를 들어, int에서 long으로 다음과 같이 변환하는 경우 이는 Java에서처럼 암시적입니다.

C#
int int1 = 5;
long long1 = int1;  //implicit conversion

다음은 .NET Framework 데이터 형식 사이의 암시적 변환 목록입니다.

소스 형식 대상 형식

Byte

short, ushort, int, uint, long, ulong, float, double 또는 decimal

Sbyte

short, int, long, float, double 또는 decimal

Int

long, float, double 또는 decimal

Uint

long, ulong, float, double 또는 decimal

Short

int, long, float, double 또는 decimal

Ushort

int, uint, long, ulong, float, double 또는 decimal

Long

float, double 또는 decimal

Ulong

float, double 또는 decimal

Float

double

Char

ushort, int, uint, long, ulong, float, double 또는 decimal

Java에서와 동일한 구문을 사용하여 명시적으로 변환할 식을 캐스팅할 수 있습니다.

C#
long long2 = 5483;
int int2 = (int)long2;  //explicit conversion

다음 표에는 명시적 변환이 나열되어 있습니다.

소스 형식 대상 형식

Byte

sbyte 또는 char

Sbyte

byte, ushort, uint, ulong 또는 char

Int

sbyte, byte, short, ushort, uint, ulong 또는 char

Uint

sbyte, byte, short, ushort, int 또는 char

Short

sbyte, byte, ushort, uint, ulong 또는 char

Ushort

sbyte, byte, short 또는 char

Long

sbyte, byte, short, ushort, int, uint, ulong 또는 char

Ulong

sbyte, byte, short, ushort, int, uint, long 또는 char

Float

sbyte, byte, short, ushort, int, uint, long, ulong, char 또는 decimal

Double

sbyte, byte, short, ushort, int, uint, long, ulong, char, float 또는 decimal

Char

sbyte, byte 또는 short

Decimal

sbyte, byte, short, ushort, int, uint, long, ulong, char, float 또는 double

값 형식과 참조 형식

C#은 두 가지 종류의 변수 형식을 지원합니다.

  • 값 형식

    이 형식은 구조체를 사용하여 선언한 사용자 정의 형식을 비롯하여 char, int 및 float 같이 기본 제공되는 데이터 형식입니다.

  • 참조 형식

    기본 형식으로 구성된 기타 복합 데이터 형식과 클래스입니다. 이러한 형식의 변수에는 형식의 인스턴스가 포함되지 않고 인스턴스에 대한 참조만 포함됩니다.

다음과 같이 i와 j라는 두 개의 값 형식 변수를 만드는 경우 i와 j는 서로 완전히 독립적입니다.

C#
int i = 10;
int j = 20;

이 두 변수에는 개별 메모리 위치가 할당됩니다.

값 형식에 대한 별도의 메모리 주소

이러한 변수 중 하나의 값을 변경해도 다른 변수는 영향을 받지 않습니다. 예를 들어, 다음과 같은 식이 있는 경우 변수 사이에는 아무런 연관이 없습니다.

C#
int k = i;

즉, i의 값을 변경해도 k의 값은 i의 값을 처음 할당할 때와 동일하게 유지됩니다.

C#
i = 30;

System.Console.WriteLine(i.ToString());  // 30
System.Console.WriteLine(k.ToString());  // 10

그러나 참조 형식이 동작하는 방식은 다릅니다. 예를 들어, 다음과 같이 두 변수를 선언할 수 있습니다.

C#
Employee ee1 = new Employee();
Employee ee2 = ee1;

클래스는 C#에서 참조 형식이므로 ee1Employee에 대한 참조로 알려집니다. 위의 두 줄에서 첫 번째 줄은 Employee의 인스턴스를 메모리에 만들고 ee1이 이를 참조하도록 설정합니다. 따라서 ee2ee1와 같은 값으로 설정하면 메모리의 클래스에 대해 복제된 참조가 포함됩니다. ee2에 대한 속성을 변경하면 이러한 변경 내용이 ee1의 속성에 반영됩니다. 두 변수 모두 다음과 같이 메모리의 동일한 개체를 가리키기 때문입니다.

참조 형식에 대한 메모리 위치

Boxing 및 Unboxing

값 형식을 참조 형식으로 변환하는 프로세스를 boxing이라고 합니다. 반대로 참조 형식을 값 형식으로 변환하는 프로세스를 unboxing이라고 합니다. 다음 코드에서는 이 과정을 보여 줍니다.

C#
int i = 123;      // a value type
object o = i;     // boxing
int j = (int)o;  // unboxing

Java에서는 이러한 변환을 수동으로 수행해야 합니다. 래퍼 클래스의 개체를 구성하여, 즉 boxing을 통해 기본 데이터 형식을 래퍼 클래스의 개체로 변환할 수 있습니다. 마찬가지로 래퍼 클래스의 개체에 대한 적절한 메서드를 호출하여, 즉 unboxing을 통해 래퍼 클래스의 개체에서 기본 데이터 형식의 값을 추출할 수 있습니다. boxing과 unboxing에 대한 자세한 내용은 Boxing 변환(C# 프로그래밍 가이드)이나 Unboxing 변환(C# 프로그래밍 가이드)을 참조하십시오.

참고 항목

태그 :


Page view tracker