safe
safe (C# Reference)
safe 컨텍스트 키워드는 갱신된 메모리 안전성 모델이 안전성 선택을 명시적으로 하라고 요구하는 자리에서, 해당 선언이 건전(sound)함을 보증해 주는 키워드예요. 컴파일러가 스스로 안전성을 판별하지 못하는 선언 — 예를 들어 extern 멤버나 명시적·확장 레이아웃을 쓰는 타입의 필드 같은 곳 — 에 수정자(modifier)로 붙여서 씁니다. 이 safe 수정자는 unsafe와 짝을 이루는데요, safe는 호출자가 unsafe 컨텍스트 없이 써도 된다는 걸 보증하는 반면, unsafe는 안전성 감사 의무를 호출자에게 전파하는 역할을 해요.
[!IMPORTANT]
safe키워드는 갱신된 메모리 안전성 모델의 일부로, C# 15와 .NET 11의 미리 보기 기능이에요. 이 구문을 쓰려면LangVersion을preview로 설정해야 합니다. 명시적인safe나unsafe선택, requires-unsafe 호출자 의무를 포함한 갱신된 규칙까지 강제하려면updated-memory-safety-rules컴파일러 기능을 활성화하세요. 활성화 방법은 갱신된 메모리 안전성 규칙 활성화에서, 전체 설계는 메모리 안전성 기능 사양에서 확인할 수 있어요.
본문
Extern 멤버
extern 멤버는 네이티브 코드를 호출하기 때문에 컴파일러가 그 안전성을 판별할 수 없어요. 그래서 갱신된 모델 아래에서는 LibraryImport 부분 메서드를 포함한 모든 extern 선언에 safe나 unsafe를 명시해야 합니다:
// Syntax requires LangVersion preview; enforcement requires the updated-memory-safety-rules compiler feature.
[LibraryImport("libc")]
internal static safe partial int getpid();
[LibraryImport("libc", StringMarshalling = StringMarshalling.Utf8)]
internal static unsafe partial nint strlen(byte* str);
getpid는 매개 변수를 받지 않고 기본형(primitive)을 반환하니까, 작성자가 이 호출이 안전하다고 보증하고 호출자는 unsafe 컨텍스트 없이 쓸 수 있어요. 반면 strlen은 네이티브 코드가 역참조하는 원시 포인터(raw pointer)를 받으므로 unsafe 선언이고, 그 의무는 호출자에게 전파되죠. 갱신된 규칙을 활성화하면 두 수정자를 모두 생략하는 건 오류가 됩니다.
명시적·확장 레이아웃 필드
[StructLayout(LayoutKind.Explicit)]나 [ExtendedLayout]가 붙은 타입에서는 컴파일러가 모든 인스턴스 필드의 안전성을 스스로 판별하지 못해요. 그래서 그런 필드마다 safe나 unsafe를 명시해야 합니다:
// Syntax requires LangVersion preview; enforcement requires the updated-memory-safety-rules compiler feature.
[StructLayout(LayoutKind.Explicit)]
internal struct Union
{
[FieldOffset(0)]
internal safe int AsInt;
[FieldOffset(0)]
internal safe float AsFloat;
}
네이티브 포인터를 담거나, 타입 시스템이 표현하지 못하는 불변 조건(invariant)을 지니는 타입의 필드는 unsafe예요. 반대로 타입 시스템이 완전히 기술하는 타입의 필드는 safe죠. 이 규칙은 명시적 레이아웃 필드와 확장 레이아웃 필드에 똑같이 적용됩니다. extern 멤버와 마찬가지로, 갱신된 규칙을 활성화하면 두 수정자를 모두 생략하는 건 오류가 돼요.
C# 언어 사양
더 자세한 내용은 C# 언어 사양의 안전하지 않은 코드를 참고하세요. 언어 사양은 C# 구문과 사용법의 결정적 출처이에요.
갱신된 메모리 안전성 모델의 설계에 대해서는 메모리 안전성 기능 사양을 보세요.