메모
이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.
기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련LDM(언어 디자인 모임) 노트에서 캡처됩니다.
사양문서에서 기능 스펙렛을 C# 언어 표준으로 채택하는 프로세스에 대해 자세히 알아볼 수 있습니다.
챔피언 이슈: https://github.com/dotnet/csharplang/issues/2765
요약
문들의 일련이 namespace_member_declaration앞에서 compilation_unit의 일부로 발생하는 것을 허용합니다 (즉, 소스 파일).
해당 의미론은 문 시퀀스이 있을 경우, 실제 메서드 이름을 제외한 형식 선언이 다음과 같이 출력된다는 것입니다.
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
https://github.com/dotnet/csharplang/issues/3117참조하세요.
동기
명시적 Main
메서드가 필요하기 때문에 가장 간단한 프로그램조차도 일정량의 상용구가 있습니다. 이것은 언어 학습 및 프로그램 명확성의 방해가되는 것 같습니다. 따라서 이 기능의 주요 목표는 학습자와 코드의 명확성을 위해 불필요한 상용구 없이 C# 프로그램을 허용하는 것입니다.
상세 디자인
통사론
유일한 추가 구문은 컴파일 단위 내에서 namespace_member_declarations 바로 앞에 문s의 시퀀스를 허용하는 것입니다.
compilation_unit
: extern_alias_directive* using_directive* global_attributes? statement* namespace_member_declaration*
;
compilation_unit 하나만 문을수 있습니다.
본보기:
if (args.Length == 0
|| !int.TryParse(args[0], out int n)
|| n < 0) return;
Console.WriteLine(Fib(n).curr);
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (curr, prev) = Fib(i - 1);
return (curr + prev, curr);
}
의미론
프로그램의 컴파일 단위에 최상위 문이 있는 경우 의미는 다음과 같이 전역 네임스페이스에 있는 Program
클래스의 Main
메서드 블록 본문에 결합된 것과 같습니다.
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
형식의 이름은 "Program"이므로 소스 코드에서 이름으로 참조할 수 있습니다. 부분 형식이므로 소스 코드에서 "Program"이라는 형식도 부분 형식으로 선언해야 합니다.
그러나 메서드 이름 "Main"은 설명 목적으로만 사용되며 컴파일러에서 사용하는 실제 이름은 구현에 따라 달라지며 소스 코드에서 이름으로 메서드를 참조할 수 없습니다.
메서드는 프로그램의 진입점으로 지정됩니다. 규칙에 따라 진입점 후보로 간주될 수 있는 명시적으로 선언된 메서드는 무시됩니다. 이 경우 경고가 보고됩니다. 최상위 문이 있는 경우 -main:<type>
컴파일러 스위치를 지정하는 것은 오류입니다.
진입점 메서드에는 항상 string[] args
하나의 공식 매개 변수가 있습니다. 실행 환경에서는 애플리케이션이 시작될 때 지정된 명령줄 인수가 포함된 string[]
인수를 만들고 전달합니다.
string[]
인수는 null이 아니지만 명령줄 인수가 지정되지 않은 경우 길이가 0일 수 있습니다. 'args' 매개 변수는 최상위 문 내의 범위에 있으며 외부 범위에 있지 않습니다. 일반적인 이름 충돌 및 그림자 규칙이 적용됩니다.
비동기 작업은 일반 비동기 진입점 메서드 내의 명령문에서 허용되는 수준까지 최상위 명령문에서 허용됩니다. 그러나 await
식 및 기타 비동기 작업을 생략하면 필요하지는 않지만, 경고가 생성되지 않습니다.
생성된 진입점 메서드의 서명은 다음과 같이 최상위 문에서 사용하는 작업에 따라 결정됩니다.
비동기 연산\값과 함께 반환 | 프레젠테이션 | 없음 |
---|---|---|
프레젠테이션 | static Task<int> Main(string[] args) |
static Task Main(string[] args) |
없음 | static int Main(string[] args) |
static void Main(string[] args) |
위의 예제에서는 다음 $Main
메서드 선언을 생성합니다.
partial class Program
{
static void $Main(string[] args)
{
if (args.Length == 0
|| !int.TryParse(args[0], out int n)
|| n < 0) return;
Console.WriteLine(Fib(n).curr);
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (curr, prev) = Fib(i - 1);
return (curr + prev, curr);
}
}
}
동시에 다음과 같은 예제가 있습니다.
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
다음을 산출합니다.
partial class Program
{
static async Task $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
}
}
다음과 같은 예:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
다음과 같은 결과를 낼 수 있습니다.
partial class Program
{
static async Task<int> $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
}
}
다음과 같은 예가 있습니다.
System.Console.WriteLine("Hi!");
return 2;
는 다음을 생성합니다.
partial class Program
{
static int $Main(string[] args)
{
System.Console.WriteLine("Hi!");
return 2;
}
}
최상위 지역 변수 및 로컬 함수의 범위
최상위 지역 변수 및 함수는 생성된 진입점 메서드로 "래핑"되지만 모든 컴파일 단위에서 프로그램 전체의 범위에 있어야 합니다. 간단한 이름 평가를 위해 전역 네임스페이스에 도달하면 다음을 수행합니다.
- 먼저 생성된 진입점 메서드 내에서 이 시도가 실패한 경우에만 이름을 평가하려고 시도합니다.
- 전역 네임스페이스 선언 내에서 "일반" 평가가 수행됩니다.
이로 인해 전역 네임스페이스 내에 선언된 네임스페이스 및 형식의 이름이 가려질 수 있으며, 가져온 이름의 가림 현상도 발생할 수 있습니다.
단순 이름 평가가 최상위 문 외부에서 발생하고 평가에서 최상위 지역 변수 또는 함수를 생성하는 경우 오류가 발생합니다.
이러한 방식으로 "최상위 함수"(https://github.com/dotnet/csharplang/issues/3117시나리오 2)를 더 잘 처리할 수 있는 향후 기능을 보호하고 실수로 지원한다고 생각하는 사용자에게 유용한 진단을 제공할 수 있습니다.
C# feature specifications