다음을 통해 공유


7가지 기본 개념

7.1 애플리케이션 시작

프로그램은 다른 애플리케이션의 일부로 사용할 클래스 라이브러리 또는 직접 시작할 수 있는 애플리케이션으로 컴파일될 수 있습니다. 이 컴파일 모드를 결정하는 메커니즘은 구현 정의이며 이 사양의 외부입니다.

애플리케이션으로 컴파일된 프로그램에는 다음 요구 사항을 충족하여 진입점으로 한정되는 하나 이상의 메서드가 포함되어야 합니다.

  • 이름을 Main갖습니다.
  • 그것은 될 것이다 static.
  • 제네릭은 아닙니다.
  • 제네릭이 아닌 형식으로 선언되어야 합니다. 메서드를 선언하는 형식이 중첩 형식인 경우 해당 바깥쪽 형식은 제네릭이 아닐 수 있습니다.
  • 메서드의 반환 형식 async 이 <a0System.Threading.Tasks.Task/>인 경우 한정자가 있을 수 있습니다.
  • 반환 형식은 , void, int또는 System.Threading.Tasks.Task.이어야 System.Threading.Tasks.Task<int>합니다.
  • 구현 없이는 부분 메서드(§15.6.9)가 아니어야 합니다.
  • 매개 변수 목록은 비어 있거나 형식 string[]의 단일 값 매개 변수가 있어야 합니다.

참고: 한정자가 있는 async 메서드는 진입점으로 한정하려면 위에 지정된 두 반환 형식 중 하나만 있어야 합니다. async void 메서드 또는 async 다른 대기 가능 형식을 반환하는 메서드(예: ValueTask 진입점으로 한정되지 ValueTask<int> 않음)입니다. 끝 메모

프로그램 내에서 진입점으로 한정된 메서드가 두 개 이상인 경우 외부 메커니즘을 사용하여 애플리케이션의 실제 진입점으로 간주되는 메서드를 지정할 수 있습니다. 반환 형식이 있거나 반환 형식 int 이 있는 한정 메서드가 발견되면 반환 형식 void 이 있거나 System.Threading.Tasks.Task 진입점 메서드로 간주되지 않는 정규화된 메서드 System.Threading.Tasks.Task<int> 입니다. 프로그램이 정확히 하나의 진입점 없이 애플리케이션으로 컴파일되는 것은 컴파일 시간 오류입니다. 클래스 라이브러리로 컴파일된 프로그램에는 애플리케이션 진입점으로 한정되는 메서드가 포함될 수 있지만 결과 라이브러리에는 진입점이 없습니다.

일반적으로 메서드의 선언된 접근성(§7.5.2)은 선언에 지정된 액세스 한정자(§15.3.6)에 의해 결정되며, 마찬가지로 형식의 선언된 접근성은 선언에 지정된 액세스 한정자에 의해 결정됩니다. 지정된 형식의 지정된 메서드를 호출할 수 있게 하려면 형식과 멤버 모두에 액세스할 수 있어야 합니다. 그러나 애플리케이션 진입점은 특별한 경우입니다. 특히 실행 환경은 선언된 접근성과 바깥쪽 형식 선언의 선언된 접근성에 관계없이 애플리케이션의 진입점에 액세스할 수 있습니다.

진입점 메서드의 반환 형식이 System.Threading.Tasks.Task 또는 System.Threading.Tasks.Task<int>경우 컴파일러는 해당 Main 메서드를 호출하는 동기 진입점 메서드를 합성해야 합니다. 합성된 메서드에는 메서드를 기반으로 하는 매개 변수 및 반환 형식이 Main 있습니다.

  • 합성된 메서드의 매개 변수 목록은 메서드의 Main 매개 변수 목록과 동일합니다.
  • 메서드의 Main 반환 형식이 System.Threading.Tasks.Task면 합성된 메서드의 반환 형식은 void
  • 메서드의 Main 반환 형식이 System.Threading.Tasks.Task<int>면 합성된 메서드의 반환 형식은 int

합성된 메서드의 실행은 다음과 같이 진행됩니다.

  • 합성된 메서드는 메서드를 호출하여 Main 메서드에 string[] 이러한 매개 변수가 있는 경우 Main 해당 매개 변수 값을 인수로 전달합니다.
  • 메서드가 Main 예외를 throw하는 경우 예외는 합성된 메서드에 의해 전파됩니다.
  • 그렇지 않으면 합성된 진입점은 반환된 작업이 완료될 때까지 기다렸다가 매개 변수가 없는 인스턴스 메서드 또는 §C.3GetAwaiter().GetResult()확장 메서드를 사용하여 작업을 호출 합니다. 작업이 실패 GetResult() 하면 예외가 throw되고 이 예외는 합성된 메서드에 의해 전파됩니다.
  • Main 반환 형식이 있는 메서드의 System.Threading.Tasks.Task<int>경우 작업이 성공적으로 int 완료되면 반환된 GetResult() 값이 합성된 메서드에서 반환됩니다.

애플리케이션의 유효 진입점은 프로그램 내에서 선언된 진입점이거나 위에서 설명한 대로 필요한 경우 합성된 메서드입니다. 따라서 유효 진입점의 반환 형식은 항상 void 또는 int.

애플리케이션이 실행되면 새 애플리케이션 도메인 이 만들어집니다. 애플리케이션의 여러 인스턴스화가 동시에 동일한 컴퓨터에 있을 수 있으며 각각 자체 애플리케이션 도메인이 있습니다. 애플리케이션 도메인은 애플리케이션 상태의 컨테이너 역할을 하여 애플리케이션 격리를 가능하게 합니다. 애플리케이션 도메인은 애플리케이션 및 애플리케이션에서 사용하는 클래스 라이브러리에 정의된 형식에 대한 컨테이너 및 경계 역할을 합니다. 한 애플리케이션 도메인에 로드된 형식은 다른 애플리케이션 도메인에 로드된 동일한 형식과 구별되며 개체 인스턴스는 애플리케이션 도메인 간에 직접 공유되지 않습니다. 예를 들어 각 애플리케이션 도메인에는 이러한 형식에 대한 정적 변수의 자체 복사본이 있으며, 형식에 대한 정적 생성자는 애플리케이션 도메인당 최대 한 번 실행됩니다. 구현은 애플리케이션 도메인을 만들고 소멸하기 위한 구현 정의 정책 또는 메커니즘을 자유롭게 제공할 수 있습니다.

애플리케이션 시작은 실행 환경에서 애플리케이션의 유효 진입점을 호출할 때 발생합니다. 유효 진입점이 매개 변수를 선언하는 경우 애플리케이션을 시작하는 동안 구현은 해당 매개 변수의 초기 값이 문자열 배열에 대한 null이 아닌 참조인지 확인해야 합니다. 이 배열은 애플리케이션을 시작하기 전에 호스트 환경에서 구현 정의 값을 제공하는 애플리케이션 매개 변수라고 하는 문자열에 대한 null이 아닌 참조로 구성되어야 합니다. 호스트된 환경의 다른 위치에서 애플리케이션을 시작하기 전에 결정된 애플리케이션 정보를 제공하려는 의도입니다.

참고: 명령줄을 지원하는 시스템에서 애플리케이션 매개 변수는 일반적으로 명령줄 인수라고 하는 것과 일치합니다. 끝 메모

유효 진입점의 반환 형식이 int면 실행 환경에 의한 메서드 호출의 반환 값이 애플리케이션 종료(§7.2)에 사용됩니다.

위에 나열된 상황 외에 진입점 메서드는 모든 측면에서 진입점이 아닌 것과 같이 동작합니다. 특히 일반 메서드 호출과 같이 애플리케이션 수명 동안 다른 지점에서 진입점이 호출되는 경우 메서드의 특수 처리는 없습니다. 매개 변수가 있는 경우 초기 값이 null있거나 null 참조가 포함된 배열을 참조하는 값이 아닐null 수 있습니다. 마찬가지로 진입점의 반환 값은 실행 환경에서 호출하는 것 외에는 특별한 의미가 없습니다.

7.2 애플리케이션 종료

애플리케이션 종료 는 실행 환경에 대한 제어를 반환합니다.

애플리케이션의 유효 진입점 메서드의 반환 형식이 int 예외 없이 실행이 완료되는 경우 반환된 값 int 은 애플리케이션의 종료 상태 코드로 사용됩니다. 이 코드의 목적은 실행 환경에 대한 성공 또는 실패 통신을 허용하는 것입니다. 유효 진입점 메서드의 반환 형식이 void 예외 없이 실행이 완료되면 종료 상태 코드가 됩니다 0.

예외(§21.4)로 인해 유효 진입점 메서드가 종료되는 경우 종료 코드는 구현으로 정의됩니다. 또한 구현은 종료 코드를 지정하기 위한 대체 API를 제공할 수 있습니다.

종료자(§15.13)가 애플리케이션 종료의 일부로 실행되는지 여부는 구현에서 정의됩니다.

참고: .NET Framework 구현은 이러한 정리가 억제되지 않은 경우(예: 라이브러리 메서드 호출에 의해) 가비지 수집되지 않은 모든 개체에 대해 종료자(GC.SuppressFinalize)를 호출하기 위해 모든 합리적인 노력을 기울입니다. 끝 메모

7.3 선언

C# 프로그램의 선언은 프로그램의 구성 요소를 정의합니다. C# 프로그램은 네임스페이스를 사용하여 구성됩니다. 형식 선언 및 중첩된 네임스페이스 선언을 포함할 수 있는 네임스페이스 선언(§14)을 사용하여 도입되었습니다. 형식 선언(§14.7)은 클래스(§15), 구조체(§16), 인터페이스(§18), 열거형(§19) 및 대리자(§20)를 정의하는 데 사용됩니다. 형식 선언에서 허용되는 멤버의 종류는 형식 선언의 형식에 따라 달라집니다. 예를 들어 클래스 선언에는 상수(§15.4), 필드(§15.5), 메서드(§15.6), 속성(§15.7), 이벤트(§15.8), 인덱서(§15.8)에 대한 선언이 포함될 수 있습니다. 9), 연산자(§15.10), 인스턴스 생성자(§15.11), 정적 생성자(§15.12), 종료자(§15.13) 및 중첩 형식(§15.3.9).

선언은 선언이 속한 선언 공간에 이름을 정의합니다. 다음 경우를 제외하고 선언 공간에 이름이 같은 멤버를 도입하는 두 개 이상의 선언이 있는 것은 컴파일 시간 오류입니다.

  • 이름이 같은 두 개 이상의 네임스페이스 선언이 동일한 선언 공간에서 허용됩니다. 이러한 네임스페이스 선언은 단일 논리 네임스페이스를 형성하고 단일 선언 공간을 공유하기 위해 집계됩니다.
  • 별도의 프로그램이지만 동일한 네임스페이스 선언 공간에 있는 선언은 동일한 이름을 공유할 수 있습니다.

    참고: 그러나 이러한 선언은 동일한 애플리케이션에 포함된 경우 모호성을 발생시킬 수 있습니다. 끝 메모

  • 이름이 같지만 고유 서명이 있는 두 개 이상의 메서드가 동일한 선언 공간(§7.6)에서 허용됩니다.
  • 이름이 같지만 형식 매개 변수의 고유 번호를 가진 두 개 이상의 형식 선언은 동일한 선언 공간(§7.8.2)에서 허용됩니다.
  • 동일한 선언 공간에 부분 한정자가 있는 두 개 이상의 형식 선언은 동일한 이름, 동일한 수의 형식 매개 변수 및 동일한 분류(클래스, 구조체 또는 인터페이스)를 공유할 수 있습니다. 이 경우 형식 선언은 단일 형식에 기여하며 자체 집계되어 단일 선언 공간(§15.2.7)을 형성합니다.
  • 형식 선언에 하나 이상의 형식 매개 변수(§7.8.2)가 있는 한 동일한 선언 공간에 있는 네임스페이스 선언 및 형식 선언은 동일한 이름을 공유할 수 있습니다.

다음에 설명된 대로 여러 가지 유형의 선언 공간이 있습니다.

  • 프로그램의 모든 컴파일 단위 내에서 바깥쪽 namespace_declaration없는 namespace_member_declaration 전역 선언 공간이라는 단일 결합 선언 공간의 멤버입니다.
  • 프로그램의 모든 컴파일 단위 내에서 정규화된 네임스페이스 이름이 동일한 namespace_declaration 내 의 namespace_member_declaration단일 결합 선언 공간의 멤버입니다.
  • compilation_unitnamespace_body 별칭 선언 공간이 있습니다. compilation_unit 또는 namespace_body 각 extern_alias_directiveusing_alias_directive 별칭 선언 공간(§14.5.2)에 멤버를 제공합니다.
  • 각 비 부분 클래스, 구조체 또는 인터페이스 선언은 새 선언 공간을 만듭니다. 각 부분 클래스, 구조체 또는 인터페이스 선언은 동일한 프로그램의 일치하는 모든 파트가 공유하는 선언 공간에 기여합니다(§16.2.4). 이름은 class_member_declaration, struct_member_declaration, interface_member_declaration 또는 type_parameter 통해 이 선언 공간에 도입됩니다. 오버로드된 인스턴스 생성자 선언 및 정적 생성자 선언을 제외하고 클래스 또는 구조체는 클래스 또는 구조체와 이름이 같은 멤버 선언을 포함할 수 없습니다. 클래스, 구조체 또는 인터페이스는 오버로드된 메서드 및 인덱서의 선언을 허용합니다. 또한 클래스 또는 구조체는 오버로드된 인스턴스 생성자 및 연산자의 선언을 허용합니다. 예를 들어 클래스, 구조체 또는 인터페이스는 이러한 메서드 선언이 시그니처(§7.6)와 다른 경우 이름이 같은 여러 메서드 선언을 포함할 수 있습니다. 기본 클래스는 클래스의 선언 공간에 기여하지 않으며 기본 인터페이스는 인터페이스의 선언 공간에 기여하지 않습니다. 따라서 파생 클래스 또는 인터페이스는 상속된 멤버와 이름이 같은 멤버를 선언할 수 있습니다. 이러한 멤버는 상속된 멤버를 숨깁니다.
  • 각 대리자 선언은 새 선언 공간을 만듭니다. 이름은 매개 변수(fixed_parameter 및 parameter_array)와 type_parameter통해 이 선언 공간에 도입됩니다.
  • 각 열거형 선언은 새 선언 공간을 만듭니다. 이름은 enum_member_declarations 통해 이 선언 공간에 도입됩니다.
  • 각 메서드 선언, 속성 선언, 속성 접근자 선언, 인덱서 선언, 인덱서 접근자 선언, 연산자 선언, 인스턴스 생성자 선언, 익명 함수 및 로컬 함수는 지역 변수 선언 공간이라는 새 선언 공간을 만듭니다. 이름은 매개 변수(fixed_parameter 및 parameter_array)와 type_parameter통해 이 선언 공간에 도입됩니다. 속성 또는 인덱서의 set 접근자는 이름을 value 매개 변수로 도입합니다. 함수 멤버, 무명 함수 또는 로컬 함수의 본문(있는 경우)은 지역 변수 선언 공간 내에 중첩된 것으로 간주됩니다. 지역 변수 선언 공간과 중첩된 지역 변수 선언 공간에 이름이 같은 요소가 있는 경우 중첩된 로컬 이름의 범위 내에서 중첩된 로컬 이름으로 외부 로컬 이름이 숨겨집니다(§7.7.1).
  • 추가 지역 변수 선언 공간은 멤버 선언, 익명 함수 및 로컬 함수 내에서 발생할 수 있습니다. 이름은 패턴 s, declaration_expression, declaration_statement 및 exception_specifier 통해 이러한 선언 공간에 도입됩니다. 지역 변수 선언 공간은 중첩될 수 있지만 지역 변수 선언 공간과 중첩된 지역 변수 선언 공간에 동일한 이름의 요소가 포함되는 것은 오류입니다. 따라서 중첩된 선언 공간 내에서는 매개 변수, 형식 매개 변수, 지역 변수, 로컬 함수 또는 상수와 이름이 같은 지역 변수, 로컬 함수 또는 상수를 바깥쪽 선언 공간에서 선언할 수 없습니다. 두 선언 공간 모두 다른 선언 공간을 포함하지 않는 한 두 선언 공간에 동일한 이름의 요소가 포함될 수 있습니다. 로컬 선언 공간은 다음 구문에 의해 생성됩니다.
    • 필드 및 속성 선언의 각 variable_initializer 다른 지역 변수 선언 공간 내에 중첩되지 않은 고유한 지역 변수 선언 공간을 도입합니다.
    • 함수 멤버, 무명 함수 또는 로컬 함수의 본문(있는 경우)은 함수의 지역 변수 선언 공간 내에 중첩된 것으로 간주되는 지역 변수 선언 공간을 만듭니다.
    • constructor_initializer 인스턴스 생성자 선언 내에 중첩된 지역 변수 선언 공간을 만듭니다. 생성자 본문에 대한 지역 변수 선언 공간은 차례로 이 지역 변수 선언 공간 내에 중첩됩니다.
    • 블록, switch_block, specific_catch_clause, iteration_statement 및 using_statement 중첩된 지역 변수 선언 공간을 만듭니다.
    • statement_list 직접 속하지 않는 각 embedded_statement 중첩된 지역 변수 선언 공간을 만듭니다.
    • switch_section 중첩된 지역 변수 선언 공간을 만듭니다. 그러나 switch_section statement_list 내에서 직접 선언된 변수(statement_list 내의 중첩된 지역 변수 선언 공간 내에 있지 않음)는 switch_section 대신 바깥쪽 switch_block 지역 변수 선언 공간에 직접 추가됩니다.
    • query_expression 구문 변환(§12.20.3)은 하나 이상의 람다 식을 도입할 수 있습니다. 익명 함수로서 각 함수는 위에서 설명한 대로 지역 변수 선언 공간을 만듭니다.
  • 블록 또는 switch_block 레이블에 대한 별도의 선언 공간을 만듭니다. 이름은 labeled_statement통해 이 선언 공간에 도입되고 이름은 goto_statement통해 참조됩니다. 블록의 레이블 선언 공간에중첩된 블록이 포함됩니다. 따라서 중첩된 블록 내에서는 바깥쪽 블록의 레이블과 이름이 같은 레이블을 선언할 수 없습니다.

참고: switch_section 내에서 직접 선언된 변수가 switch_section 대신 switch_block 지역 변수 선언 공간에 추가된다는 사실은 놀라운 코드로 이어질 수 있습니다. 아래 예제에서 지역 변수 y 는 case 0의 스위치 섹션에 표시되는 선언에도 불구하고 기본 사례에 대한 switch 섹션 내의 범위에 있습니다. 지역 변수 z 는 선언이 발생하는 스위치 섹션의 지역 변수 선언 공간에 도입되므로 기본 사례에 대한 switch 섹션 내의 범위에 없습니다.

int x = 1;
switch (x)
{
    case 0:
        int y;
        break;
    case var z when z < 10:
        break;
    default:
        y = 10;
        // Valid: y is in scope
        Console.WriteLine(x + y);
        // Invalid: z is not scope
        Console.WriteLine(x + z);
        break;
}

끝 메모

이름이 선언되는 텍스트 순서는 일반적으로 의미가 없습니다. 특히 텍스트 순서는 네임스페이스, 상수, 메서드, 속성, 이벤트, 인덱서, 연산자, 인스턴스 생성자, 종료자, 정적 생성자 및 형식의 선언 및 사용에 중요하지 않습니다. 선언 순서는 다음과 같은 방법으로 중요합니다.

  • 필드 선언에 대한 선언 순서는 이니셜라이저(있는 경우)가 실행되는 순서를 결정합니다(§15.5.6.2, §15.5.6.3).
  • 지역 변수는 사용 전에 정의해야 합니다(§7.7).
  • 열거형 멤버 선언(§19.4)의 선언 순서는 constant_expression 값을 생략할 때 중요합니다.

: 네임스페이스의 선언 공간은 "개방형 종료됨"이며 정규화된 이름이 같은 두 네임스페이스 선언이 동일한 선언 공간에 기여합니다. 예를 들어

namespace Megacorp.Data
{
    class Customer
    {
        ...
    }
}

namespace Megacorp.Data
{
    class Order
    {
        ...
    }
}

위의 두 네임스페이스 선언은 동일한 선언 공간에 기여합니다. 이 경우 정규화된 이름과 Megacorp.Data.CustomerMegacorp.Data.Order2개의 클래스를 선언합니다. 두 선언이 동일한 선언 공간에 기여하기 때문에 각 선언에 동일한 이름의 클래스 선언이 포함된 경우 컴파일 시간 오류가 발생했습니다.

끝 예제

참고: 위에서 지정한 대로 블록의 선언 공간에는 중첩된 블록이 포함됩니다. 따라서 다음 예제 F 에서 이름과 G 메서드는 이름이 i 외부 블록에 선언되고 내부 블록에서 다시 선언될 수 없으므로 컴파일 시간 오류가 발생합니다. 그러나 HI'는 별도의 중첩되지 않은 블록에 선언되므로 메서드와 i 메서드는 유효합니다.

class A
{
    void F()
    {
        int i = 0;
        if (true)
        {
            int i = 1;
        }
    }

    void G()
    {
        if (true)
        {
            int i = 0;
        }
        int i = 1;
    }

    void H()
    {
        if (true)
        {
            int i = 0;
        }
        if (true)
        {
            int i = 1;
        }
    }

    void I()
    {
        for (int i = 0; i < 10; i++)
        {
            H();
        }
        for (int i = 0; i < 10; i++)
        {
            H();
        }
    }
}

끝 메모

7.4 멤버

7.4.1 일반

네임스페이스와 형식에는 멤버가 있습니다.

참고: 엔터티의 멤버는 일반적으로 엔터티에 대한 참조로 시작하는 정규화된 이름과 "." 토큰, 멤버 이름을 사용하여 사용할 수 있습니다. 끝 메모

형식의 멤버는 형식 선언에서 선언되거나 형식의 기본 클래스에서 상속됩니다 . 형식이 기본 클래스에서 상속되는 경우 인스턴스 생성자, 종료자 및 정적 생성자를 제외한 기본 클래스의 모든 멤버는 파생 형식의 멤버가 됩니다. 기본 클래스 멤버의 선언된 접근성은 멤버가 상속되는지 여부를 제어하지 않습니다. 상속은 인스턴스 생성자, 정적 생성자 또는 종료자가 아닌 멤버로 확장됩니다.

그러나 상속된 멤버는 예를 들어 선언된 접근성(§7.5.2)으로 인해 파생 형식에서 액세스할 수 없을 수 있습니다. 끝 메모

7.4.2 네임스페이스 멤버

바깥쪽 네임스페이스가 없는 네임스페이스 및 형식은 전역 네임스페이스의 멤버입니다. 이는 전역 선언 공간에 선언된 이름에 직접 해당합니다.

네임스페이스 내에 선언된 네임스페이스 및 형식은 해당 네임스페이스의 멤버입니다. 이는 네임스페이스의 선언 공간에 선언된 이름에 직접 해당합니다.

네임스페이스에는 액세스 제한이 없습니다. 프라이빗, 보호된 네임스페이스 또는 내부 네임스페이스를 선언할 수 없으며 네임스페이스 이름은 항상 공개적으로 액세스할 수 있습니다.

7.4.3 구조체 멤버

구조체의 멤버는 구조체에 선언된 멤버와 구조체의 직접 기본 클래스 및 간접 기본 System.ValueType클래스 object 에서 상속된 멤버입니다.

단순 형식의 멤버는 단순 형식(§8.3.5)으로 별칭이 지정된 구조체 형식의 멤버에 직접 해당합니다.

7.4.4 열거형 멤버

열거형의 멤버는 열거형에 선언된 상수와 열거형의 직접 기본 클래스 및 간접 기본 System.Enum 클래스 System.ValueType 및에서 상속된 멤버입니다object.

7.4.5 클래스 멤버

클래스의 멤버는 클래스에서 선언된 멤버 및 기본 클래스에서 상속된 멤버입니다(기본 클래스가 없는 클래스 object 제외). 기본 클래스에서 상속된 멤버에는 기본 클래스의 상수, 필드, 메서드, 속성, 이벤트, 인덱서, 연산자 및 형식이 포함되지만 기본 클래스의 인스턴스 생성자, 종료자 및 정적 생성자는 포함되지 않습니다. 기본 클래스 멤버는 접근성과 관계없이 상속됩니다.

클래스 선언에는 상수, 필드, 메서드, 속성, 이벤트, 인덱서, 연산자, 인스턴스 생성자, 종료자, 정적 생성자 및 형식의 선언이 포함될 수 있습니다.

(object) 및 (string)의 멤버 는 별칭이 있는 클래스 형식의 멤버에 직접 해당합니다.

7.4.6 인터페이스 멤버

인터페이스의 멤버는 인터페이스 및 인터페이스의 모든 기본 인터페이스에서 선언된 멤버입니다.

참고: 클래스 object 의 멤버는 엄밀히 말하면 모든 인터페이스의 멤버가 아닙니다(§18.4). 그러나 클래스 object 의 멤버는 모든 인터페이스 형식(§12.5)의 멤버 조회를 통해 사용할 수 있습니다. 끝 메모

7.4.7 배열 멤버

배열의 멤버는 클래스 System.Array에서 상속된 멤버입니다.

7.4.8 대리자 구성원

대리자는 클래스 System.Delegate에서 멤버를 상속합니다. 또한 선언(Invoke)에 지정된 동일한 반환 형식 및 매개 변수 목록을 사용하여 명명 된 메서드를 포함합니다. 이 메서드의 호출은 동일한 대리자 인스턴스의 대리자 호출(§20.6)과 동일하게 동작해야 합니다.

구현은 상속을 통해 또는 대리자 자체에서 직접 추가 멤버를 제공할 수 있습니다.

7.5 멤버 액세스

7.5.1 일반

멤버 선언을 사용하면 멤버 액세스를 제어할 수 있습니다. 멤버의 접근성은 멤버의 선언된 접근성(§7.5.2)과 즉시 포함된 형식의 접근성(있는 경우)에 의해 설정됩니다.

특정 멤버에 대한 액세스가 허용되면 멤버에 액세스할 수 있다고 합니다. 반대로 특정 멤버에 대한 액세스가 허용되지 않으면 멤버에 액세스할 수 없다고 합니다. 액세스가 이루어지는 텍스트 위치가 멤버의 접근성 도메인(§7.5.3)에 포함된 경우 멤버에 대한 액세스가 허용됩니다.

7.5.2 선언된 접근성

멤버의 선언된 접근성다음 중 하나일 수 있습니다.

  • 공용- 멤버 선언에 한정자를 public 포함하여 선택합니다. 직관적인 public 의미는 "액세스가 제한되지 않음"입니다.
  • 보호됨- 멤버 선언에 한정자를 protected 포함하여 선택됩니다. 직관적인 의미 protected 는 "포함하는 클래스 또는 포함하는 클래스에서 파생된 형식으로 제한되는 액세스"입니다.
  • 내부- 멤버 선언에 한정자를 internal 포함하여 선택합니다. 직관적인 의미 internal 는 "이 어셈블리로 제한되는 액세스"입니다.
  • 보호된 내부- 멤버 선언에 a와 한 protectedinternal 정자를 모두 포함하여 선택됩니다. 직관적인 의미 protected internal 는 "포함하는 클래스에서 파생된 형식뿐만 아니라 이 어셈블리 내에서 액세스할 수 있습니다."입니다.
  • 멤버 선언에 a와 privateprotected 정자를 모두 포함하여 선택한 private protected입니다. 직관적인 의미 private protected 는 "포함하는 클래스 및 포함하는 클래스에서 파생된 형식을 통해 이 어셈블리 내에서 액세스할 수 있습니다."
  • Private- 멤버 선언에 한정자를 private 포함하여 선택합니다. 직관적인 의미 private 는 "포함된 형식으로 제한된 액세스"입니다.

멤버 선언이 발생하는 컨텍스트에 따라 특정 형식의 선언된 접근성만 허용됩니다. 또한 멤버 선언에 액세스 한정자가 포함되지 않은 경우 선언이 수행되는 컨텍스트에 따라 기본 선언된 접근성이 결정됩니다.

  • 네임스페이 public 스는 암시적으로 접근성을 선언했습니다. 네임스페이스 선언에는 액세스 한정자가 허용되지 않습니다.
  • 컴파일 단위 또는 네임스페이스에서 직접 선언된 형식(다른 형식과는 달리)에는 접근성을 포함 public 하거나 internal 선언할 수 있으며 기본적으로 선언된 접근성을 사용할 internal 수 있습니다.
  • 클래스 멤버는 허용된 종류의 선언된 접근성을 가질 수 있으며 기본적으로 선언된 접근성을 가질 private 수 있습니다.

    참고: 클래스의 멤버로 선언된 형식은 허용된 종류의 선언된 접근성을 가질 수 있는 반면, 네임스페이스의 멤버로 선언된 형식에는 접근성만 있거나 public 선언된 접근성만 internal 있을 수 있습니다. 끝 메모

  • 구조체는 암시적으로 봉인되므로 구조체 멤버는 액세스 가능성을 선언하거나 선언된 접근성을 가질 수 있으며 기본적으로 public 선언된 접근성을 가질 internalprivateprivate수 있습니다. (즉, 해당 구조체에서 상속되지 않음)에 struct 도입된 구조체 멤버는 접근성 또는 protected 선언된 접근성을 가질 protected internalprivate protected수 없습니다.

    참고: 구조체의 멤버로 선언된 형식은 접근성 또는 선언된 접근성을 가질 publicinternalprivate수 있지만 네임스페이스의 멤버로 선언된 형식에는 접근성만 있거나 public 선언된 접근성만 internal 있을 수 있습니다. 끝 메모

  • 인터페이스 멤버는 public 암시적으로 접근성을 선언했습니다. 인터페이스 멤버 선언에는 액세스 한정자가 허용되지 않습니다.
  • 열거형 멤버는 public 암시적으로 접근성을 선언했습니다. 열거형 멤버 선언에는 액세스 한정자가 허용되지 않습니다.

7.5.3 접근성 도메인

멤버의 접근성 도메인멤버에 대한 액세스가 허용되는 프로그램 텍스트의 (연결되지 않은) 섹션으로 구성됩니다. 멤버의 접근성 도메인을 정의하기 위해 멤버는 형식 내에서 선언되지 않은 경우 최상위 수준이라고 하며 멤버는 다른 형식 내에서 선언된 경우 중첩되었다고 합니다. 또한 프로그램의 프로그램 텍스트는 프로그램의 모든 컴파일 단위에 포함된 모든 텍스트로 정의되며 형식의 프로그램 텍스트는 해당 형식의 type_declaration포함된 모든 텍스트(형식 내에 중첩된 형식 포함)로 정의됩니다.

미리 정의된 형식(예: object또는intdouble)의 접근성 도메인은 무제한입니다.

프로그램에서 T 선언된 최상위 언바운드 형식 (P)의 접근성 도메인은 다음과 같이 정의됩니다.

  • 선언된 접근성 T 이 public이면 접근성 도메인 T 은 프로그램 텍스트 P 및 참조하는 모든 프로그램입니다 P.
  • 선언된 접근성 T 이 내부인 경우 접근성 도메인은 T .의 P프로그램 텍스트입니다.

참고: 이러한 정의에서 최상위 언바운드 형식의 접근성 도메인은 항상 해당 형식이 선언된 프로그램의 프로그램 텍스트 이상입니다. 끝 메모

생성된 형식에 대한 접근성 도메인은 바인딩되지 않은 제네릭 형식 T<A₁, ..., Aₑ> 의 접근성 도메인과 형식 T 인수 A₁, ..., Aₑ의 접근성 도메인의 교집합입니다.

프로그램 M내의 형식 T 에 선언된 중첩 멤버 P 의 접근성 도메인은 다음과 같이 정의됩니다(형식일 수 있음M).

  • M에 대해 선언된 액세스 가능성이 public인 경우 M의 액세스 가능 도메인은 T의 액세스 가능 도메인입니다.
  • 선언된 접근성 M 이 있는 protected internal경우 외부에 선언D된 모든 형식PT 프로그램 텍스트와 프로그램 텍스트의 결합을 허용합니다P. 접근성 도메인 M 은 .의 접근성 도메인과 교집합됩니다 TD.
  • 선언된 접근성이 있는 M경우 프로그램 텍스트와 프로그램 텍스트 private protected 와 파생된 D모든 형식의 P 교집합이 되도록 합니다T.T 접근성 도메인 M 은 .의 접근성 도메인과 교집합됩니다 TD.
  • 선언된 접근성 M 이 있는 protectedD경우 프로그램 텍스트와 파생된 T모든 형식의 T프로그램 텍스트의 합자입니다. 접근성 도메인 M 은 .의 접근성 도메인과 교집합됩니다 TD.
  • M에 대해 선언된 액세스 가능성이 internal인 경우 M의 액세스 가능 도메인은 T의 프로그램 텍스트를 가진 P의 액세스 가능 도메인의 교집합 부분입니다.
  • M에 대해 선언된 액세스 가능성이 private인 경우 M의 액세스 가능 도메인은 T의 프로그램 텍스트입니다.

참고: 이러한 정의에서 중첩 멤버의 접근성 도메인은 항상 최소한 멤버가 선언된 형식의 프로그램 텍스트입니다. 또한 멤버의 접근성 도메인이 멤버가 선언된 형식의 접근성 도메인보다 더 포괄적이지는 않습니다. 끝 메모

참고: 직관적인 용어로 형식 또는 멤버 M 에 액세스할 때 다음 단계를 평가하여 액세스가 허용되는지 확인합니다.

  • 먼저 컴파일 단위 또는 네임스페이스가 아닌 형식 내에서 선언된 경우 M 해당 형식에 액세스할 수 없는 경우 컴파일 시간 오류가 발생합니다.
  • 그런 다음, 있는 M경우 public 액세스가 허용됩니다.
  • 그렇지 않으면 선언된 프로그램 M 내에서 발생하거나 파생 클래스 형식(protected internal)을 통해 선언되고 수행되는 클래스에서 파생된 클래스 M 내에서 발생하는 경우 M액세스가 허용됩니다.
  • 그렇지 않은 경우 Mprotected선언된 클래스 내에서 발생하거나 파생 클래스 M 형식(M)을 통해 선언되고 수행되는 클래스에서 파생된 클래스 내에서 발생하는 경우 액세스가 허용됩니다.
  • 그렇지 않으면 Minternal선언된 프로그램 M 내에서 발생하는 경우 액세스가 허용됩니다.
  • 그렇지 않으면 Mprivate선언된 형식 M 내에서 발생하는 경우 액세스가 허용됩니다.
  • 그렇지 않으면 형식 또는 멤버에 액세스할 수 없으며 컴파일 시간 오류가 발생합니다. 끝 메모

예제: 다음 코드에서

public class A
{
    public static int X;
    internal static int Y;
    private static int Z;
}

internal class B
{
    public static int X;
    internal static int Y;
    private static int Z;

    public class C
    {
        public static int X;
        internal static int Y;
        private static int Z;
    }

    private class D
    {
        public static int X;
        internal static int Y;
        private static int Z;
    }
}

클래스 및 멤버에는 다음과 같은 접근성 도메인이 있습니다.

  • 액세스 가능성 도메인은 AA.X 무제한입니다.
  • 포함 프로그램의 프로그램 텍스트인 , A.Y, BB.X, B.Y, B.CB.C.X 의 접근성 도메인B.C.Y입니다.
  • 의 접근성 도메인은 .의 A.ZA프로그램 텍스트입니다.
  • 의 액세스 가능성 도메인 B.Z 이며 B.D , 프로그램 텍스트 및 .의 B프로그램 텍스트를 B.CB.D포함합니다.
  • 의 접근성 도메인은 .의 B.C.ZB.C프로그램 텍스트입니다.
  • 의 액세스 가능성 도메인 B.D.X 이며 B.D.Y , 프로그램 텍스트 및 .의 B프로그램 텍스트를 B.CB.D포함합니다.
  • 의 접근성 도메인은 .의 B.D.ZB.D프로그램 텍스트입니다. 예제에서 알 수 있듯이 멤버의 접근성 도메인은 포함하는 형식보다 크지 않습니다. 예를 들어 모든 X 멤버에 퍼블릭 선언된 접근성이 있더라도 포함하는 형식으로 제한되는 접근성 도메인을 제외한 A.X 모든 멤버가 있습니다.

끝 예제

§7.4설명된 대로 인스턴스 생성자, 종료자 및 정적 생성자를 제외한 기본 클래스의 모든 멤버는 파생 형식에 의해 상속됩니다. 여기에는 기본 클래스의 프라이빗 멤버도 포함됩니다. 그러나 프라이빗 멤버의 접근성 도메인에는 멤버가 선언된 형식의 프로그램 텍스트만 포함됩니다.

예제: 다음 코드에서

class A
{
    int x;

    static void F(B b)
    {
        b.x = 1;         // Ok
    }
}

class B : A
{
    static void F(B b)
    {
        b.x = 1;         // Error, x not accessible
    }
}

클래스는 B 클래스에서 프라이빗 멤버 x 를 상속합니다 A . 멤버는 프라이빗이므로 class_bodyA만 액세스할 수 있습니다. 따라서 메서드에 b.x 대한 액세스는 A.F 성공하지만 메서드에서 B.F 실패합니다.

끝 예제

7.5.4 보호된 액세스

protected 선언된 클래스의 프로그램 텍스트 외부에 액세스하거나 private protected 인스턴스 멤버가 선언된 프로그램의 프로그램 텍스트 외부에서 인스턴스 멤버에 액세스하는 경우 protected internal 해당 멤버가 선언된 클래스에서 파생되는 클래스 선언 내에서 액세스가 수행됩니다. 또한 해당 파생 클래스 형식의 인스턴스 또는 해당 클래스 형식에서 생성된 클래스 형식을 통해 액세스가 수행되어야 합니다. 이 제한은 멤버가 동일한 기본 클래스에서 상속되는 경우에도 파생 클래스가 다른 파생 클래스의 보호된 멤버에 액세스하지 못하도록 합니다.

B 보호된 인스턴스 멤버M를 선언하는 기본 클래스를 만들고 DB. class_bodyD내에서 액세스 권한은 M 다음 형식 중 하나를 사용할 수 있습니다.

  • 양식의 정규화되지 않은 type_name 또는 M.
  • 형식 또는 D
  • base.M.
  • 양식 base[ primary_expression].

이러한 형태의 액세스 외에도 파생 클래스는 constructor_initializer 기본 클래스의 보호된 인스턴스 생성자(§15.11.2)에 액세스할 수 있습니다.

예제: 다음 코드에서

public class A
{
    protected int x;

    static void F(A a, B b)
    {
        a.x = 1; // Ok
        b.x = 1; // Ok
    }
}

public class B : A
{
    static void F(A a, B b)
    {
        a.x = 1; // Error, must access through instance of B
        b.x = 1; // Ok
    }
}

내에서A는 둘 다 x 의 인스턴스를 통해 액세스할 A 수 있으며B, 두 경우 모두 액세스는 인스턴스 또는 파생 클래스A를 통해A기 때문에 가능합니다. 그러나 내에서B는 .에서 x파생되지 않으므로 인스턴스 AA통해 액세스할 B 수 없습니다.

끝 예제

예제:

class C<T>
{
    protected T x;
}

class D<T> : C<T>
{
    static void F()
    {
        D<T> dt = new D<T>();
        D<int> di = new D<int>();
        D<string> ds = new D<string>();
        dt.x = default(T);
        di.x = 123;
        ds.x = "test";
    }
}

여기서 세 가지 x 할당은 모두 제네릭 형식에서 생성된 클래스 형식의 인스턴스를 통해 수행되므로 허용됩니다.

끝 예제

참고: 제네릭 클래스에 선언된 보호된 멤버의 접근성 도메인(§7.5.3)에는 해당 제네릭 클래스에서 생성된 모든 형식에서 파생된 모든 클래스 선언의 프로그램 텍스트가 포함됩니다. 예제:

class C<T>
{
    protected static T x;
}

class D : C<string>
{
    static void Main()
    {
        C<int>.x = 5;
    }
}

클래스 protected 가 .에서 C<int>.x파생되더라도 멤버 DD 대한 참조 C<string> 가 유효합니다. 끝 메모

7.5.5 접근성 제약 조건

C# 언어의 여러 구문에는 최소한 멤버 또는 다른 형식만큼 형식에 액세스할 수 있어야 합니다. 접근성 도메인이 접근성 도메인 TM 의 상위 집합인 경우 형식 T 은 최소한 멤버 또는 형식 M 만큼 액세스할 수 있다고 합니다. 즉, T 액세스할 수 있는 모든 컨텍스트에서 액세스할 수 있는 M 것처럼 TM 액세스할 수 있습니다.

다음과 같은 접근성 제약 조건이 있습니다.

  • 클래스 형식의 직접 기본 클래스는 적어도 클래스 형식 자체만큼 액세스할 수 있어야 합니다.
  • 인터페이스 형식의 명시적 기본 인터페이스는 적어도 인터페이스 형식 자체만큼 액세스할 수 있어야 합니다.
  • 대리자 형식의 반환 형식 및 매개 변수 형식은 적어도 대리자 형식 자체만큼 액세스할 수 있어야 합니다.
  • 상수의 형식은 상수 자체만큼 액세스 가능해야 합니다.
  • 필드의 형식은 적어도 필드 자체만큼 액세스할 수 있어야 합니다.
  • 메서드의 반환 형식 및 매개 변수 형식은 적어도 메서드 자체만큼 액세스할 수 있어야 합니다.
  • 속성의 유형은 적어도 속성 자체만큼 액세스 할 수 있어야합니다.
  • 이벤트의 유형은 적어도 이벤트 자체만큼 액세스할 수 있어야 합니다.
  • 인덱서의 형식 및 매개 변수 형식은 적어도 인덱서 자체만큼 액세스할 수 있어야 합니다.
  • 연산자의 반환 형식 및 매개 변수 형식은 적어도 연산자 자체만큼 액세스할 수 있어야 합니다.
  • 인스턴스 생성자의 매개 변수 형식은 적어도 인스턴스 생성자 자체만큼 액세스할 수 있어야 합니다.
  • 형식 매개 변수의 인터페이스 또는 클래스 형식 제약 조건은 적어도 제약 조건을 선언하는 멤버만큼 액세스할 수 있어야 합니다.

예제: 다음 코드에서

class A {...}
public class B: A {...}

클래스는 B 적어도 에 액세스할 수 A없으므로 B 컴파일 시간 오류가 발생합니다.

끝 예제

: 마찬가지로 다음 코드에서

class A {...}

public class B
{
    A F() {...}
    internal A G() {...}
    public A H() {...}
}

H 반환 형식 BA 메서드만큼 액세스할 수 없으므로 메서드의 경우 컴파일 시간 오류가 발생합니다.

끝 예제

7.6 서명 및 오버로드

메서드, 인스턴스 생성자, 인덱서 및 연산자는 시그니처로 특징지어집니다.

  • 메서드의 서명은 메서드 이름, 형식 매개 변수 수, 각 매개 변수의 형식 및 매개 변수 전달 모드로 구성되며 왼쪽에서 오른쪽으로 순서대로 고려됩니다. 이러한 목적을 위해 매개 변수 형식에서 발생하는 메서드의 모든 형식 매개 변수는 해당 이름이 아니라 메서드의 형식 매개 변수 목록에서 서수 위치로 식별됩니다. 메서드의 서명에는 특히 반환 형식, 매개 변수 이름, 형식 매개 변수 이름, 형식 매개 변수 제약 조건, params 매개 변수 한정자 또는 this 매개 변수가 필요한지 선택적 매개 변수가 포함되지 않습니다.
  • 인스턴스 생성자의 서명은 왼쪽에서 오른쪽으로 순서대로 고려되는 각 매개 변수의 형식 및 매개 변수 전달 모드로 구성됩니다. 인스턴스 생성자의 서명에는 특히 가장 적합한 매개 변수에 대해 지정될 수 있는 한정자나 매개 변수가 필요한지 선택적인지 여부가 포함되지 params 않습니다.
  • 인덱서의 서명은 왼쪽에서 오른쪽 순서로 고려되는 각 매개 변수의 형식으로 구성됩니다. 인덱서의 서명에는 특히 요소 형식이 포함되지 않으며 가장 적합한 매개 변수에 대해 지정될 수 있는 한정자도 포함되지 않으며 매개 변수가 필요한지 선택 사항인지 여부도 포함되지 params 않습니다.
  • 연산자의 서명은 연산자의 이름과 각 매개 변수의 형식으로 구성되며 왼쪽에서 오른쪽으로 순서대로 고려됩니다. 특히 연산자의 서명에는 결과 형식이 포함되지 않습니다.
  • 변환 연산자의 서명은 원본 형식과 대상 형식으로 구성됩니다. 변환 연산자의 암시적 또는 명시적 분류는 서명의 일부가 아닙니다.
  • 동일한 멤버 종류(메서드, 인스턴스 생성자, 인덱서 또는 연산자)의 두 시그니처는 이름, 형식 매개 변수 수, 매개 변수 수 및 매개 변수 전달 모드가 같고 해당 매개 변수 형식(§10.2.2) 간에 ID 변환이 존재하는 경우 동일한 서명으로 간주됩니다.

서명은 클래스, 구조체 및 인터페이스에서 멤버를 오버로드하기 위한 사용 메커니즘입니다.

  • 메서드의 오버로드를 사용하면 해당 클래스, 구조체 또는 인터페이스 내에서 시그니처가 고유할 경우 클래스, 구조체 또는 인터페이스에서 동일한 이름의 여러 메서드를 선언할 수 있습니다.
  • 인스턴스 생성자의 오버로드를 사용하면 해당 시그니처가 해당 클래스 또는 구조체 내에서 고유할 경우 클래스 또는 구조체가 여러 인스턴스 생성자를 선언할 수 있습니다.
  • 인덱서의 오버로드를 사용하면 해당 시그니처가 해당 클래스, 구조체 또는 인터페이스 내에서 고유할 경우 클래스, 구조체 또는 인터페이스에서 여러 인덱서를 선언할 수 있습니다.
  • 연산자의 오버로드를 사용하면 해당 시그니처가 해당 클래스 또는 구조체 내에서 고유할 경우 클래스 또는 구조체가 동일한 이름을 가진 여러 연산자를 선언할 수 있습니다.

, inout 매개 변수 한정자는 서명의 일부로 간주되지만 ref단일 형식으로 선언된 멤버는 서명에서만 in다를 outref수 없습니다. 두 메서드의 out 모든 매개 변수가 한정자로 변경 in 된 경우 두 멤버가 동일한 형식으로 선언되고 시그니처가 ref 같은 경우 컴파일 시간 오류가 발생합니다. 서명 일치(예: 숨기기 또는 재정의)in의 다른 용도로, outref 서명의 일부로 간주되며 서로 일치하지 않습니다.

참고: 이 제한은 C# 프로그램을 CLI(공용 언어 인프라)에서 쉽게 번역할 수 있도록 하기 위한 것이며, 이는 단독으로 다른 inoutref메서드를 정의하는 방법을 제공하지 않습니다. 끝 메모

형식 object 이며 dynamic 서명을 비교할 때 구분되지 않습니다. 따라서 시그니처를 대체 object 해야만 다른 단일 형식으로 dynamic 선언된 멤버는 허용되지 않습니다.

예제: 다음 예제에서는 해당 서명과 함께 오버로드된 메서드 선언 집합을 보여 줍니다.

interface ITest
{
    void F();                   // F()
    void F(int x);              // F(int)
    void F(ref int x);          // F(ref int)
    void F(out int x);          // F(out int) error
    void F(object o);           // F(object)
    void F(dynamic d);          // error.
    void F(int x, int y);       // F(int, int)
    int F(string s);            // F(string)
    int F(int x);               // F(int) error
    void F(string[] a);         // F(string[])
    void F(params string[] a);  // F(string[]) error
    void F<S>(S s);             // F<0>(0)
    void F<T>(T t);             // F<0>(0) error
    void F<S,T>(S s);           // F<0,1>(0)
    void F<T,S>(S s);           // F<0,1>(1) ok
}

모든 in, outref 매개 변수 한정자(§15.6.2)는 서명의 일부입니다. 따라서 , F(int), F(in int)F(out int)F(ref int) 모두 고유한 서명입니다. 그러나 F(in int)해당 F(out int) 서명은 , 및 F(ref int)in에 의해outref서만 다르므로 동일한 인터페이스 내에서 선언할 수 없습니다. 또한 반환 형식과 params 한정자는 서명의 일부가 아니므로 반환 형식 또는 한정자의 포함 또는 제외 params 에 따라 오버로드할 수 없습니다. 따라서 위에서 식별된 메서드 F(int)F(params string[]) 의 선언으로 인해 컴파일 시간 오류가 발생합니다. 끝 예제

7.7 범위

7.7.1 일반

이름의 범위이름을 한정하지 않고 이름으로 선언된 엔터티를 참조할 수 있는 프로그램 텍스트 영역입니다. 범위를 중첩수 있으며 내부 범위는 외부 범위에서 이름의 의미를 다시 지정할 수 있습니다. (그러나 중첩된 블록 내에서 지역 변수 또는 지역 상수와 이름이 같은 지역 변수 또는 지역 상수를 바깥쪽 블록에 선언할 수 없다는 것을 §7.3에 의해 부과된 제한을 제거하지는 않습니다.) 그런 다음 외부 범위의 이름은 내부 범위에서 다루는 프로그램 텍스트 영역에 숨겨지게 되며, 외부 이름에 대한 액세스는 이름을 한정해야만 가능합니다.

  • 바깥쪽 namespace_declaration 없는 namespace_member_declaration(§14.6)로 선언 네임스페이스 멤버의 범위는 전체 프로그램 텍스트입니다.

  • 정규화된 이름이 있는 namespace_declaration 내에서 namespace_member_declaration 선언한 네임스페이스 멤버의 범위는 N정규화된 이름이 거나 마침표로 시작하는 모든 N namespace_body.N

  • extern_alias_directive 정의된 이름 범위(§14.4)는 compilation_unit 또는 namespace_body포함하는 using_directive, global_attributesnamespace_member_declaration 범위로 확장 됩니다. extern_alias_directive 기본 선언 공간에 새 멤버를 제공하지 않습니다. 즉, extern_alias_directive 전이적이 아니라 compilation_unit 또는 발생하는 namespace_body 영향을 줍니다.

  • using_directive 정의하거나 가져온 이름 범위(§14.5)는 using_directive 발생하는 compilation_unit 또는 namespace_body global_attributes 및 namespace_member_declaration 범위로 확장됩니다. using_directive 특정 compilation_unit 또는 namespace_body 내에서 0개 이상의 네임스페이스 또는 형식 이름을 사용할 수 있지만 새 멤버를 기본 선언 공간에 제공하지는 않습니다. 즉, using_directive 전이적이 아니라 발생하는 compilation_unit 또는 namespace_body 영향을 줍니다.

  • class_declaration type_parameter_list선언된 형식 매개 변수의 범위(§15.2)는 해당 class_declaration class_base, type_parameter_constraints_clausesclass_body.

    참고: 클래스의 멤버와 달리 이 범위는 파생 클래스로 확장되지 않습니다. 끝 메모

  • struct_declaration type_parameter_list선언된 형식 매개 변수의 범위(§16.2)는 해당 struct_declaration struct_interfaces, type_parameter_constraints_clausestruct_body.

  • interface_declaration type_parameter_list선언된 형식 매개 변수의 범위(§18.2)는 해당 interface_declaration interface_base, type_parameter_constraints_clauseinterface_body.

  • delegate_declaration type_parameter_list선언된 형식 매개 변수의 범위(§20.2)는 해당 delegate_declaration return_type, parameter_listtype_parameter_constraints_clause범위입니다.

  • method_declaration type_parameter_list 선언된 형식 매개 변수의 범위(§15.6.1)는 method_declaration.

  • class_member_declaration 선언된 멤버의 범위(§15.3.1)는 선언이 발생하는 class_body. 또한 클래스 멤버의 범위는 멤버의 접근성 도메인(§7.5.3)에 포함된 파생 클래스의 class_body 확장 됩니다.

  • struct_member_declaration 선언된 멤버의 범위(§16.3)는 선언이 발생하는 struct_body.

  • enum_member_declaration 선언된 멤버의 범위(§19.4)는 선언이 발생하는 enum_body.

  • method_declaration 선언된 매개 변수의 범위(§15.6)는 해당 method_declaration method_body 또는 ref_method_body.

  • indexer_declaration 선언된 매개 변수의 범위(§15.9)는 해당 indexer_declaration indexer_body.

  • operator_declaration 선언된 매개 변수의 범위(§15.10)는 해당 operator_declaration operator_body.

  • constructor_declaration 선언된 매개 변수의 범위(§15.11)는 해당 constructor_declaration constructor_initializer 블록 입니다.

  • lambda_expression 선언된 매개 변수의 범위(§12.19)는 해당 lambda_expression lambda_expression_body.

  • anonymous_method_expression 선언된 매개 변수의 범위(§12.19)는 해당 anonymous_method_expression 블록입니다.

  • labeled_statement 선언된 레이블의 범위(§13.5)는 선언이 발생하는 블록입니다.

  • local_variable_declaration 선언된 지역 변수의 범위(§13.6.2)는 선언이 발생하는 블록입니다.

  • 문(§13.8.3)의 switch_block 선언된 지역 변수의 switch범위는 switch_block.

  • 문(§13.9.4for

  • local_constant_declaration 선언된 로컬 상수의 범위(§13.6.3)는 선언이 발생하는 블록입니다. constant_declarator 앞에 있는 텍스트 위치에서 로컬 상수의 참조를 참조하는 것은 컴파일 시간 오류입니다.

  • foreach_statement, using_statement, lock_statement 또는 query_expression 일부로 선언된 변수의 범위는 지정된 구문의 확장에 따라 결정됩니다.

네임스페이스, 클래스, 구조체 또는 열거형 멤버의 범위 내에서 멤버 선언 앞에 오는 텍스트 위치에서 멤버를 참조할 수 있습니다.

예제:

class A
{
    void F()
    {
        i = 1;
    }

    int i = 0;
}

여기서는 선언되기 전에 참조하는 F 것이 유효 i 합니다.

끝 예제

지역 변수의 범위 내에서 선언자 앞에 있는 텍스트 위치에서 지역 변수를 참조하는 것은 컴파일 시간 오류입니다.

예제:

class A
{
    int i = 0;

    void F()
    {
        i = 1;                // Error, use precedes declaration
        int i;
        i = 2;
    }

    void G()
    {
        int j = (j = 1);     // Valid
    }

    void H()
    {
        int a = 1, b = ++a; // Valid
    }
}

F 위의 메서드에서 특히 첫 번째 할당 i 은 외부 범위에 선언된 필드를 참조하지 않습니다. 대신 지역 변수를 참조하고 변수 선언 앞에 텍스트로 표시되므로 컴파일 시간 오류가 발생합니다. G 메서드에서 선언 j 에 대한 이니셜라이저의 사용 j 은 선언자 앞에 사용되지 않으므로 유효합니다. H 메서드에서 후속 선언자는 동일한 local_variable_declaration 내에서 이전 선언자에 선언된 지역 변수를 올바르게 참조합니다.

끝 예제

참고: 지역 변수 및 지역 상수에 대한 범위 지정 규칙은 식 컨텍스트에서 사용되는 이름의 의미가 블록 내에서 항상 동일하도록 설계되었습니다. 지역 변수의 범위가 선언에서 블록 끝까지만 확장되는 경우 위의 예제에서 첫 번째 할당은 인스턴스 변수에 할당되고 두 번째 할당은 지역 변수에 할당되며, 블록의 문이 나중에 다시 정렬될 경우 컴파일 시간 오류가 발생할 수 있습니다.)

블록 내 이름의 의미는 이름이 사용되는 컨텍스트에 따라 다를 수 있습니다. 예제에서

class A {}

class Test
{
    static void Main()
    {
        string A = "hello, world";
        string s = A;                      // expression context
        Type t = typeof(A);                // type context
        Console.WriteLine(s);              // writes "hello, world"
        Console.WriteLine(t);              // writes "A"
    }
}

이름은 A 식 컨텍스트에서 지역 변수 A 를 참조하고 형식 컨텍스트에서 클래스 A를 참조하는 데 사용됩니다.

끝 메모

7.7.2 이름 숨기기

7.7.2.1 일반

엔터티의 범위는 일반적으로 엔터티의 선언 공간보다 더 많은 프로그램 텍스트를 포함합니다. 특히 엔터티의 범위에는 동일한 이름의 엔터티를 포함하는 새 선언 공간을 도입하는 선언이 포함될 수 있습니다. 이러한 선언으로 인해 원래 엔터티가 숨겨집니다. 반대로 엔터티는 숨겨지지 않을 때 볼있다고 합니다.

이름 숨기기는 범위가 중첩을 통해 겹치고 범위가 상속을 통해 겹치는 경우에 발생합니다. 두 가지 유형의 숨기기 특성은 다음 하위 클래스에 설명되어 있습니다.

7.7.2.2 중첩을 통해 숨기기

네임스페이스 내에서 네임스페이스 또는 형식을 중첩한 결과, 클래스 또는 구조체 내의 형식 중첩, 로컬 함수 또는 람다의 결과, 매개 변수, 지역 변수 및 로컬 상수 선언의 결과로 중첩을 통해 이름이 숨김이 발생할 수 있습니다.

예제: 다음 코드에서

class A
{
    int i = 0;
    void F()
    {
        int i = 1;

        void M1()
        {
            float i = 1.0f;
            Func<double, double> doubler = (double i) => i * 2.0;
        }
    }

    void G()
    {
        i = 1;
    }
}

메서드 내에서 F 인스턴스 변수는 지역 변수 ii에 의해 숨겨지지만 메서드 G 내에서 i 는 여전히 인스턴스 변수를 참조합니다. 로컬 함수 M1 내에서 직접 float i 실행 외부 i를 숨깁니다. 람다 매개 변수 ifloat i 람다 본문 내부를 숨깁니다.

끝 예제

내부 범위의 이름이 외부 범위의 이름을 숨기면 해당 이름의 오버로드된 모든 항목을 숨깁니다.

예제: 다음 코드에서

class Outer
{
    static void F(int i) {}
    static void F(string s) {}

    class Inner
    {
        static void F(long l) {}

        void G()
        {
            F(1); // Invokes Outer.Inner.F
            F("Hello"); // Error
        }
    }
}

호출 F(1) 은 내부 선언에 F 의해 모든 외부 발생이 Inner 숨겨지므로 선언된 항목을 호출 F 합니다. 같은 이유로 호출 F("Hello") 시 컴파일 시간 오류가 발생합니다.

끝 예제

7.7.2.3 상속을 통해 숨기기

상속을 통해 숨기는 이름은 클래스 또는 구조체가 기본 클래스에서 상속된 이름을 다시 표시할 때 발생합니다. 이 유형의 이름 숨기기는 다음 형식 중 하나를 사용합니다.

  • 클래스 또는 구조체에 도입된 상수, 필드, 속성, 이벤트 또는 형식은 이름이 같은 모든 기본 클래스 멤버를 숨깁니다.
  • 클래스 또는 구조체에 도입된 메서드는 이름이 같은 메서드가 아닌 기본 클래스 멤버와 시그니처가 동일한 모든 기본 클래스 메서드(§7.6)를 숨깁니다.
  • 클래스 또는 구조체에 도입된 인덱서는 동일한 서명(§7.6)을 가진 모든 기본 클래스 인덱서를 숨깁니다.

연산자 선언(§15.10)을 제어하는 규칙을 사용하면 파생 클래스에서 기본 클래스의 연산자와 동일한 서명을 가진 연산자를 선언할 수 없습니다. 따라서 연산자는 서로를 숨기지 않습니다.

외부 범위에서 이름을 숨기는 것과 달리 상속된 범위에서 표시되는 이름을 숨기면 경고가 보고됩니다.

예제: 다음 코드에서

class Base
{
    public void F() {}
}

class Derived : Base
{
    public void F() {} // Warning, hiding an inherited name
}

in F 선언 Derived 으로 인해 경고가 보고됩니다. 상속된 이름을 숨기는 것은 기본 클래스의 별도의 진화를 배제하기 때문에 특히 오류가 아닙니다. 예를 들어 위의 상황은 이후 버전의 Base 클래스에 없는 메서드를 도입 F 했기 때문일 수 있습니다.

끝 예제

상속된 이름을 숨김으로 인한 경고는 한정자를 사용하여 new 제거할 수 있습니다.

예제:

class Base
{
    public void F() {}
}

class Derived : Base
{
    public new void F() {}
}

new 정자는 in FDerived "new"이고 실제로 상속된 멤버를 숨기려는 것임을 나타냅니다.

끝 예제

새 멤버의 선언은 새 멤버의 범위 내에서만 상속된 멤버를 숨깁니다.

예제:

class Base
{
    public static void F() {}
}

class Derived : Base
{
    private new static void F() {} // Hides Base.F in Derived only
}

class MoreDerived : Derived
{
    static void G()
    {
        F();                       // Invokes Base.F
    }
}

위의 예제에서 in 선언 F 은 상속된 Derived값을 숨기 F 지만 새 BaseF in에는 프라이빗 액세스 권한이 있으므로 해당 범위는 확장Derived되지 MoreDerived 않습니다. 따라서 호출 F()MoreDerived.G 이 유효하고 호출 Base.F됩니다.

끝 예제

7.8 네임스페이스 및 형식 이름

7.8.1 일반

C# 프로그램의 여러 컨텍스트에서는 namespace_name 또는 type_name 지정해야 합니다.

namespace_name
    : namespace_or_type_name
    ;

type_name
    : namespace_or_type_name
    ;
    
namespace_or_type_name
    : identifier type_argument_list?
    | namespace_or_type_name '.' identifier type_argument_list?
    | qualified_alias_member
    ;

namespace_name네임스페이스를 참조하는 namespace_or_type_name.

아래 설명된 대로 해결에 따라 namespace_name namespace_or_type_name 네임스페이스를 참조하거나 컴파일 시간 오류가 발생합니다. 형식 인수(§8.4.2)는 namespace_name 있을 수 없습니다(형식만 형식 인수를 가질 수 있음).

type_name 형식을 참조하는 namespace_or_type_name. 아래 설명된 대로 해결에 따라 type_name namespace_or_type_name 형식을 참조하거나 컴파일 시간 오류가 발생합니다.

namespace_or_type_name qualified_alias_member 경우 그 의미는 §14.8.1설명되어 있습니다. 그렇지 않으면 namespace_or_type_name 다음 네 가지 형식 중 하나가 있습니다.

  • I
  • I<A₁, ..., Aₓ>
  • N.I
  • N.I<A₁, ..., Aₓ>

여기서 I 단일 식별자는 Nnamespace_or_type_name 선택 <A₁, ..., Aₓ>type_argument_list. type_argument_list 지정되지 않은 경우 0으로 간주 x 합니다.

namespace_or_type_name 의미는 다음과 같이 결정됩니다.

  • namespace_or_type_name qualified_alias_member 경우 의미는 §14.8.1지정됩니다.
  • 그렇지 않은 경우 namespace_or_type_name 폼 또는 폼 II<A₁, ..., Aₓ>의 경우:
    • 0이고 namespace_or_type_name 제네릭 메서드 선언(x) 내에 나타나지만 메서드 헤더의 특성 외부에 나타나고 해당 선언에 이름이 I 해당 형식 매개 변수를 참조합니다.
    • 그렇지 않은 경우 namespace_or_type_name 형식 선언 내에 나타나는 경우 각 인스턴스 형식 T (§15.3.2)에 대해 해당 형식 선언의 인스턴스 형식으로 시작하고 각 바깥쪽 클래스 또는 구조체 선언의 인스턴스 형식으로 계속 진행합니다(있는 경우).
      • 0이고 선언 x 에 이름이 T있는 형식 매개 변수가 포함된 경우 I namespace_or_type_name 해당 형식 매개 변수를 참조합니다.
      • 그렇지 않은 경우 namespace_or_type_name 형식 선언 의 본문 내에 나타나거나 해당 기본 형식에 이름 TI 형식 매개 변수xnamespace_or_type_name 지정된 형식 인수로 생성된 해당 형식을 참조합니다. 이러한 형식이 두 개 이상 있는 경우 더 많은 파생 형식 내에서 선언된 형식이 선택됩니다.

      참고: 형식이 아닌 멤버(상수, 필드, 메서드, 속성, 인덱서, 연산자, 인스턴스 생성자, 종료자 및 정적 생성자) 및 형식 매개 변수 수가 다른 형식 멤버는 namespace_or_type_name 의미를 결정할 때 무시됩니다. 끝 메모

    • 그렇지 않은 경우 각 네임스페이스에 N대해 namespace_or_type_name 발생하는 네임스페이스부터 시작하여 각 바깥쪽 네임스페이스(있는 경우)로 계속 진행하며 전역 네임스페이스로 끝나는 경우 엔터티가 위치할 때까지 다음 단계가 평가됩니다.
      • 0이고 x 네임스페이스의 이름이면 I 다음을 수행합니다N.
        • namespace_or_type_name 발생하는 위치가 네임스페이스 선언 으로 묶이고 네임스페이스 선언에 네임스페이스 또는 형식I 모호하고 컴파일 시간 오류가 발생합니다.
        • 그렇지 않으면 namespace_or_type_name 명명 IN된 네임스페이스를 참조합니다.
      • 그렇지 않으면 이름 NI 형식 매개 변수가 있는 액세스 가능한 형식이 포함된 경우 x 다음을 수행합니다.
        • 0이고 namespace_or_type_name 발생하는 위치가 네임스페이스 선언 x 으로 묶이고 네임스페이스 선언에 네임스페이스 또는 형식I 모호하고 컴파일 시간 오류가 발생합니다.
        • 그렇지 않으면 namespace_or_type_name 지정된 형식 인수를 사용하여 생성된 형식을 참조합니다.
      • 그렇지 않으면 namespace_or_type_name 발생하는 위치가 다음에 대한 N네임스페이스 선언으로 묶인 경우
        • 0이고 네임스페이스 선언에 가져온 네임스페이스 또는 형식과 이름을 x 연결하는 extern_alias_directive 또는 using_alias_directive 포함된경우 I namespace_or_type_name 해당 네임스페이스 또는 형식을 참조합니다.
        • 그렇지 않은 경우 네임스페이스 선언의 using_namespace_directive가져온 네임스페이스에 이름 Ix 형식 매개 변수가 있는 형식이 정확히 한 개 있는 경우 namespace_or_type_name 지정된 형식 인수로 생성된 해당 형식을 참조합니다.
        • 그렇지 않은 경우 네임스페이스 선언의 using_namespace_directive가져온 네임스페이스에 이름 Ix 형식 매개 변수가 있는 두 개 이상의 형식이 포함된 경우 namespace_or_type_name 모호하고 오류가 발생합니다.
    • 그렇지 않으면 namespace_or_type_name 정의되지 않고 컴파일 시간 오류가 발생합니다.
  • 그렇지 않으면 namespace_or_type_name 형식 또는 폼 N.IN.I<A₁, ..., Aₓ>입니다. N는 먼저 namespace_or_type_name 확인됩니다. 해결 N 에 실패하면 컴파일 시간 오류가 발생합니다. 그렇지 않으면 N.IN.I<A₁, ..., Aₓ> 다음과 같이 해결됩니다.
    • 0이고 x 네임스페이스를 참조하고 N 이름이 N있는 중첩된 네임스페이스를 포함하는 경우 I namespace_or_type_name 중첩된 네임스페이스를 참조합니다.
    • 그렇지 않은 경우 네임스페이스를 참조하고 이름 NNI 형식 매개 변수가 있는 액세스 가능한 형식을 포함하는 경우 x namespace_or_type_name 지정된 형식 인수로 생성된 해당 형식을 참조합니다.
    • 그렇지 않은 경우 (생성될 수 있는) 클래스 또는 구조체 형식을 참조하거나 N 해당 기본 클래스에 이름 NI 형식 매개 변수가 있는 중첩된 액세스 가능 형식이 포함된 경우 x namespace_or_type_name 지정된 형식 인수로 생성된 해당 형식을 참조합니다. 이러한 형식이 두 개 이상 있는 경우 더 많은 파생 형식 내에서 선언된 형식이 선택됩니다.

      참고: 해당 의미가 N.I 기본 클래스 사양 확인의 N 일부로 결정되는 경우 직접 기본 클래스 N 는 (object)로 간주됩니다 . 끝 메모

    • 그렇지 않으면 N.I 잘못된 namespace_or_type_name 컴파일 시간 오류가 발생합니다.

namespace_or_type_name 경우에만 정적 클래스(§15.2.2.4)를 참조할 수 있습니다.

  • namespace_or_type_nameTT.I 또는
  • namespace_or_type_name은(는) typeof_expression(§12.8.18)의 형태인 Ttypeof(T)입니다.

7.8.2 정규화되지 않은 이름

모든 네임스페이스 선언 및 형식 선언에는 다음과 같이 정규화되지 않은 이름이 결정됩니다.

  • 네임스페이스 선언의 경우 정규화되지 않은 이름은 선언에 지정된 qualified_identifier .
  • type_parameter_list 없는 형식 선언의 경우 정규화되지 않은 이름은 선언에 지정된 식별자입니다.
  • K개의 타입 매개변수가 있는 형식 선언의 경우, 선언에서 지정된 정규화되지 않은 이름은 식별자이며, 이는 K 타입 매개변수를 위한 generic_dimension_specifier(§12.8.18)으로 이어집니다.

7.8.3 정규화된 이름

모든 네임스페이스 및 형식 선언에는 프로그램 내의 다른 모든 항목 간에 네임스페이스 또는 형식 선언을 고유하게 식별하는 정규화된 이름이 있습니다. 정규화되지 않은 이름의 네임스페이스 또는 형식 선언의 정규화된 이름은 N 다음과 같이 결정됩니다.

  • 전역 네임스페이스의 멤버인 경우 N 정규화된 이름은 .입니다 N.
  • 그렇지 않으면 정규화된 이름은 S.NS 네임스페이스 또는 선언된 형식 선언 N 의 정규화된 이름입니다.

즉, 정규화된 이름은 N 전역 네임스페이스에서 시작하여 식별자와 generic_dimension_specifierN 전체 계층적 경로입니다. 네임스페이스 또는 형식의 모든 멤버는 고유한 이름을 가지므로 네임스페이스 또는 형식 선언의 정규화된 이름이 항상 고유합니다. 동일한 정규화된 이름이 두 개의 고유 엔터티를 참조하는 것은 컴파일 시간 오류입니다. 특히 다음 사항에 주의하십시오.

  • 네임스페이스 선언과 형식 선언 모두에 동일한 정규화된 이름을 갖는 것은 오류입니다.
  • 두 종류의 형식 선언이 동일한 정규화된 이름을 갖는 것은 오류입니다(예: 구조체 및 클래스 선언에 정규화된 이름이 동일한 경우).
  • 부분 한정자가 없는 형식 선언이 다른 형식 선언(§15.2.7)과 동일한 정규화된 이름을 갖는 것은 오류입니다.

예제: 아래 예제에서는 연결된 정규화된 이름과 함께 여러 네임스페이스 및 형식 선언을 보여 줍니다.

class A {}                 // A
namespace X                // X
{
    class B                // X.B
    {
        class C {}         // X.B.C
    }
    namespace Y            // X.Y
    {
        class D {}         // X.Y.D
    }
}
namespace X.Y              // X.Y
{
    class E {}             // X.Y.E
    class G<T>             // X.Y.G<>
    {           
        class H {}         // X.Y.G<>.H
    }
    class G<S,T>           // X.Y.G<,>
    {         
        class H<U> {}      // X.Y.G<,>.H<>
    }
}

끝 예제

7.9 자동 메모리 관리

C#에서는 자동 메모리 관리를 사용하여 개발자가 개체가 차지하는 메모리를 수동으로 할당하고 해제할 수 있습니다. 자동 메모리 관리 정책은 가비지 수집기에서 구현됩니다. 개체의 메모리 관리 수명 주기는 다음과 같습니다.

  1. 개체가 만들어지면 메모리가 할당되고, 생성자가 실행되고, 개체가 라이브로 간주됩니다.
  2. 종료자 실행 이외의 가능한 실행 연속으로 개체나 해당 인스턴스 필드에 액세스할 수 없는 경우 개체는 더 이상 사용되지 않는 것으로 간주되며 종료에 적합합니다.

    참고: C# 컴파일러와 가비지 수집기는 나중에 사용할 수 있는 개체에 대한 참조를 결정하기 위해 코드를 분석하도록 선택할 수 있습니다. 예를 들어 범위에 있는 지역 변수가 개체에 대한 유일한 기존 참조이지만 해당 지역 변수가 프로시저의 현재 실행 지점에서 실행 가능한 연속에서 참조되지 않는 경우 가비지 수집기는 개체를 더 이상 사용하지 않는 것으로 처리할 수 있습니다(하지만 필요하지는 않음). 끝 메모

  3. 개체가 종료될 수 있으면 나중에 지정되지 않은 상태로 개체에 대한 종료자(§15.13)(있는 경우)가 실행됩니다. 일반적인 상황에서는 구현 정의 API에서 이 동작을 재정의할 수 있지만 개체에 대한 종료자는 한 번만 실행됩니다.
  4. 개체의 종료자가 실행되면 종료자 실행을 포함하여 가능한 실행 연속으로 개체나 인스턴스 필드 모두에 액세스할 수 없는 경우 개체는 액세스할 수 없는 것으로 간주되고 개체는 컬렉션에 적합해집니다.

    참고: 이전에 액세스할 수 없었던 개체는 종료자로 인해 다시 액세스할 수 있습니다. 이에 대한 예는 다음과 같습니다. 끝 메모

  5. 마지막으로 개체를 수집할 수 있게 되면 가비지 수집기에서 해당 개체와 연결된 메모리를 해제합니다.

가비지 수집기는 개체 사용에 대한 정보를 유지 관리하며 이 정보를 사용하여 메모리 관리 결정(예: 메모리에서 새로 만든 개체를 찾을 위치, 개체 재배치 시기, 개체가 더 이상 사용 중이거나 액세스할 수 없는 경우)을 결정합니다.

가비지 수집기가 있다고 가정하는 다른 언어와 마찬가지로 C#은 가비지 수집기가 광범위한 메모리 관리 정책을 구현할 수 있도록 설계되었습니다. C#은 해당 범위 내의 시간 제약 조건이나 종료자가 실행되는 순서를 지정하지 않습니다. 종료자가 애플리케이션 종료의 일부로 실행되는지 여부는 구현 정의(§7.2)입니다.

가비지 수집기의 동작은 클래스 System.GC의 정적 메서드를 통해 어느 정도 제어할 수 있습니다. 이 클래스는 컬렉션 발생, 종료자 실행(또는 실행 안 됨) 등을 요청하는 데 사용할 수 있습니다.

: 가비지 수집기는 개체를 수집하고 종료자를 실행할 시기를 결정할 때 넓은 위도를 허용하므로 준수 구현은 다음 코드에 표시된 것과 다른 출력을 생성할 수 있습니다. 프로그램

class A
{
    ~A()
    {
        Console.WriteLine("Finalize instance of A");
    }
}

class B
{
    object Ref;
    public B(object o)
    {
        Ref = o;
    }

    ~B()
    {
        Console.WriteLine("Finalize instance of B");
    }
}

class Test
{
    static void Main()
    {
        B b = new B(new A());
        b = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

는 클래스의 인스턴스와 클래스 AB인스턴스를 만듭니다. 이러한 개체는 변수 b 에 값 null이 할당될 때 가비지 수집에 적합합니다. 이 시간 이후에는 사용자가 작성한 코드에서 액세스할 수 없기 때문에 이러한 개체는 가비지 수집에 적합합니다. 출력 중 하나일 수 있습니다.

Finalize instance of A
Finalize instance of B

또는

Finalize instance of B
Finalize instance of A

언어는 개체가 가비지 수집되는 순서에 제약 조건을 적용하지 않으므로

미묘한 경우 "완료 가능"과 "수집 적격"을 구분하는 것이 중요할 수 있습니다. 예를 들면 다음과 같습니다.

class A
{
    ~A()
    {
        Console.WriteLine("Finalize instance of A");
    }

    public void F()
    {
        Console.WriteLine("A.F");
        Test.RefA = this;
    }
}

class B
{
    public A Ref;

    ~B()
    {
        Console.WriteLine("Finalize instance of B");
        Ref.F();
    }
}

class Test
{
    public static A RefA;
    public static B RefB;

    static void Main()
    {
        RefB = new B();
        RefA = new A();
        RefB.Ref = RefA;
        RefB = null;
        RefA = null;
        // A and B now eligible for finalization
        GC.Collect();
        GC.WaitForPendingFinalizers();
        // B now eligible for collection, but A is not
        if (RefA != null)
        {
            Console.WriteLine("RefA is not null");
        }
    }
}

위의 프로그램에서 가비지 수집기가 종료자 A이전의 B 종료자를 실행하도록 선택하는 경우 이 프로그램의 출력은 다음과 같습니다.

Finalize instance of A
Finalize instance of B
A.F
RefA is not null

인스턴스 A 가 사용 중이 아니고 A종료자가 실행 A 되었지만 다른 종료자에서 메서드(이 경우 F)를 호출할 수 있습니다. 또한 종료자를 실행하면 개체가 메인라인 프로그램에서 다시 사용할 수 있게 될 수 있습니다. 이 경우 종료자를 실행 B하면 이전에 사용되지 않았던 인스턴스 A 가 라이브 참조 Test.RefA에서 액세스할 수 있게 되었습니다. 호출 WaitForPendingFinalizers후 인스턴스 B 는 컬렉션에 적합하지만 참조A로 인해 인스턴스 Test.RefA 가 그렇지 않습니다.

끝 예제

7.10 실행 순서

C# 프로그램의 실행은 각 실행 스레드의 부작용이 중요한 실행 지점에서 유지되도록 진행됩니다. 부작용은 휘발성 필드의 읽기 또는 쓰기, 비휘발성 변수에 대한 쓰기, 외부 리소스에 대한 쓰기 및 예외 throw로 정의됩니다. 이러한 부작용의 순서를 유지해야 하는 중요한 실행 지점은 휘발성 필드(§15.5.4lock§13.13), 스레드 생성 및 종료에 대한 참조입니다. 실행 환경은 다음 제약 조건에 따라 C# 프로그램의 실행 순서를 자유롭게 변경할 수 있습니다.

  • 데이터 의존성은 실행 스레드 내에서 유지됩니다. 즉, 각 변수의 값은 스레드의 모든 문이 원래 프로그램 순서로 실행된 것처럼 계산됩니다.
  • 초기화 순서 규칙은 유지됩니다(§15.5.5, §15.5.6).
  • 부작용의 순서는 휘발성 읽기 및 쓰기(§15.5.4)와 관련하여 유지됩니다. 또한 해당 식의 값이 사용되지 않고 필요한 부작용이 생성되지 않는다고 추론할 수 있는 경우 실행 환경에서 식의 일부를 평가할 필요가 없습니다(메서드를 호출하거나 휘발성 필드에 액세스하여 발생하는 작업 포함). 비동기 이벤트(예: 다른 스레드에서 throw된 예외)로 인해 프로그램 실행이 중단되는 경우 관찰 가능한 부작용이 원래 프로그램 순서로 표시되는 것은 아닙니다.