다음을 통해 공유


C# 형식 시스템

팁 (조언)

소프트웨어 개발이 새로운가요? 먼저 시작하기 자습서부터 시작하세요. 프로그램 작성 과정을 단계별로 안내하고, 진행하면서 타입을 소개합니다.

다른 언어로 경험하신 적 있나요? 형식 시스템을 이미 이해하고 있는 경우 값과 참조 구분을 건너뛰고 형식 가이드 종류를 선택한 다음 특정 형식에 대한 문서로 이동합니다.

C#은 엄격한 형식의 언어입니다. 모든 변수, 상수 및 식에는 형식이 있습니다. 컴파일러는 코드의 모든 작업이 관련된 형식에 대해 유효한지 확인하여 형식 보안을 적용합니다. 예를 들어 두 int 값을 추가할 수 있지만 다음을 추가할 intbool수는 없습니다.

int a = 5;
int b = a + 2; // OK

bool test = true;

// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
// int c = a + test;

비고

C 및 C++와 달리 C#에서는 bool을(를) int로 변환할 수 없습니다.

형식 안전은 컴파일 시간에, 코드가 실행되기 전에 오류를 잡습니다. 또한 컴파일러는 실행 파일에 형식 정보를 메타데이터로 포함하며, CLR(공용 언어 런타임)은 런타임 시 추가 보안 검사에 사용합니다.

형식을 사용하여 변수 선언

변수를 선언할 때 해당 형식을 명시적으로 지정하거나 var 컴파일러가 할당된 값에서 형식을 유추할 수 있도록 합니다.

// Explicit type:
int count = 10;
double temperature = 36.6;

// Compiler-inferred type:
var name = "C#";
var items = new List<string> { "one", "two", "three" };

메서드 매개 변수 및 반환 값에도 형식이 있습니다. 다음 메서드는 stringint를 사용하여 string를 반환합니다.

static string GetGreeting(string name, int visitCount)
{
    return visitCount switch
    {
        1 => $"Welcome, {name}!",
        _ => $"Welcome back, {name}! Visit #{visitCount}."
    };
}

변수를 선언한 후에는 해당 형식을 변경하거나 선언된 형식과 호환되지 않는 값을 할당할 수 없습니다. 값을 다른 형식으로 변환할 수 있습니다. 컴파일러는 데이터를 자동으로 손실하지 않는 암시적 변환 을 수행합니다. 명시적 변환 (캐스트)을 사용하려면 코드에서 변환을 나타내야 합니다. 자세한 내용은 캐스팅 및 형식 변환을 참조하세요.

기본 제공 형식 및 사용자 지정 형식

C#은 일반 데이터에 대한 기본 제공 형식(정수, 부동 소수점 숫자, boolcharstring.)을 제공합니다. 모든 C# 프로그램은 추가 참조 없이 이러한 기본 제공 형식을 사용할 수 있습니다.

기본 제공 형식 외에도 여러 구문을 사용하여 고유한 형식을 만들 수 있습니다.

  • 클래스 - 모델링 동작 및 복합 개체에 대한 참조 형식입니다. 상속 및 다형성을 지원합니다.
  • 구조체 - 작고 가벼운 데이터에 대한 값 형식입니다. 각 변수는 자체 복사본을 보유합니다.
  • 레코드 - 컴파일러에서 생성된 동등성이 있는 클래스 또는 구조체 및 ToString 표현을 통한 비파괴적 변형입니다.
  • 인터페이스 - 클래스 또는 구조체가 구현할 수 있는 멤버를 정의하는 계약입니다.
  • 열거형 — 요일이나 파일 액세스 모드처럼 정수 상수의 명명된 집합입니다.
  • 튜플 - 명명된 형식을 정의하지 않고 관련 값을 그룹화한 경량 구조 형식입니다.
  • 제네릭 - List<T>Dictionary<TKey, TValue>와 같은 형식 매개변수가 있는 구조로, 형식 안전성을 제공하여 기존 논리를 다른 형식에도 재사용합니다.

값 형식 및 참조 형식

C#의 모든 형식은 값 형식 또는 참조 형식입니다. 이러한 구분은 변수가 데이터를 저장하는 방법과 할당의 작동 방식을 결정합니다.

값 형식 은 해당 데이터를 직접 보유합니다. 새 변수에 값 형식을 할당하면 런타임에서 데이터를 복사합니다. 한 변수에 대한 변경 내용은 다른 변수에 영향을 주지 않습니다. 구조체, 열거형 및 기본 제공 숫자 형식은 모두 값 형식입니다.

참조 형식 은 관리되는 힙의 개체에 대한 참조를 보유합니다. 새 변수에 참조 형식을 할당하면 두 변수가 모두 동일한 개체를 가리킵니다. 한 변수를 통한 변경 내용은 다른 변수를 통해 표시됩니다. 클래스, 배열, 대리자 및 문자열은 참조 형식입니다.

다음 예제에서는 차이점을 보여 있습니다. 첫 번째 블록은 값 형식인 Coords 레코드 구조체에 대한 정의를 표시합니다. 두 번째 블록은 값 형식 및 참조 형식에 대한 다양한 동작을 보여줍니다.

public readonly record struct Coords(int X, int Y);
// Value type: each variable holds its own copy
var point1 = new Coords(3, 4);
var point2 = point1;
Console.WriteLine($"point1: ({point1.X}, {point1.Y})");
Console.WriteLine($"point2: ({point2.X}, {point2.Y})");
// point1 and point2 are independent copies

// Reference type: both variables refer to the same object
var list1 = new List<int> { 1, 2, 3 };
var list2 = list1;
list2.Add(4);
Console.WriteLine($"list1 count: {list1.Count}"); // 4 — same object

모든 형식은 궁극적으로 .에서 System.Object파생됩니다. 값 형식은 System.ValueType에서 파생되며, System.ValueType는 에서 파생됩니다. 이 통합 계층을 CTS( 공용 형식 시스템 )라고 합니다. 상속에 대한 자세한 내용은 상속을 참조 하세요.

형식 종류 선택

새 형식을 정의할 때 선택하는 종류는 코드가 동작하는 방식을 셰이프합니다. 초기 결정을 내리려면 다음 지침을 따르세요.

  • 튜플 - 명명된 형식이나 동작이 필요하지 않은 값의 임시 그룹화입니다.
  • struct 또는 record struct - 작은 데이터(약 64바이트 이하), 값 의미 체계 또는 불변성입니다. 레코드 구조체는 값 기반 같음 및 with 식을 추가합니다.
  • record class - 주로 값 기반의 동등성과 비파괴적 변이가 있는 데이터입니다. ToString 상속을 지원합니다.
  • class - 복잡한 동작, 다형성 또는 변경 가능한 상태입니다. 대부분의 사용자 지정 형식은 클래스입니다.
  • interface — 관련 없는 형식이 구현할 수 있는 계약입니다. ID가 아닌 기능을 정의합니다.
  • enum — 상태 코드 또는 옵션과 같은 명명된 상수의 고정 집합입니다.

두 개 이상의 옵션은 종종 합리적입니다.

컴파일 시간 형식 및 런타임 형식

변수는 컴파일 시간 및 런타임에 서로 다른 형식을 가질 수 있습니다. 컴파일 시간 형식은 소스 코드에서 선언되거나 유추된 형식입니다. 런타임 형식은 변수가 참조하는 인스턴스의 실제 형식입니다. 런타임 형식은 컴파일 시간 형식 또는 해당 형식에서 파생되거나 구현되는 형식과 동일해야 합니다. 런타임 형식에서 컴파일 시간 형식으로 ID, 참조, boxing 또는 숫자 변환과 같은 암시적 변환이 존재할 때만 할당이 유효합니다.

// Compile-time and run-time types match:
string message = "Hello, world!";

// Compile-time type differs from run-time type:
object boxed = "This is a string at run time";
IEnumerable<char> characters = "abcdefghijklmnopqrstuvwxyz";

앞의 예제 boxed 에서 컴파일 시간 형식 object 은 있지만 런타임 형식은 입니다 string. string 에서 object 이 파생되기 때문에 할당이 작동합니다. 마찬가지로 컴파일 시간에 characters의 유형은 IEnumerable<char>이며, string의 할당이 올바르게 작동합니다. 이는 string가 해당 인터페이스를 구현하기 때문입니다. 컴파일 시간 형식은 오버로드 확인 및 사용 가능한 변환을 제어합니다. 런타임 형식은 가상 메서드 디스패치, is 식 및 switch 식을 제어합니다.

참고하십시오

C# 언어 사양

자세한 내용은 C# 언어 사양을 참조하세요. 언어 사양은 C# 구문 및 사용의 최종 소스입니다.