비고
이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.
기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련된 언어 디자인 모임(LDM) 노트에 기록되어 있습니다.
사양에 관한 문서에서 기능 스펙릿을 C# 언어 표준으로 채택하는 과정에 대해 자세히 알아볼 수 있습니다.
챔피언 문제: https://github.com/dotnet/csharplang/issues/8637
요약
형식 이름을 요구하지 않고 한정자를 사용하여 람다 매개 변수를 선언할 수 있습니다. 예를 들어 (ref entry) =>
대신 (ref FileSystemEntry entry) =>
.
다른 예제로, 이 대리자를 지정합니다.
delegate bool TryParse<T>(string text, out T result);
이 간소화된 매개 변수 선언을 허용합니다.
TryParse<int> parse1 = (text, out result) => Int32.TryParse(text, out result);
현재 이 값만 유효합니다.
TryParse<int> parse2 = (string text, out int result) => Int32.TryParse(text, out result);
상세 디자인
문법
변경 내용이 없습니다. 최신 람다 문법 다음과 같습니다.
lambda_expression
: modifier* identifier '=>' (block | expression)
| attribute_list* modifier* type? lambda_parameter_list '=>' (block | expression)
;
lambda_parameter_list
: lambda_parameters (',' parameter_array)?
| parameter_array
;
lambda_parameter
: identifier
| attribute_list* modifier* type? identifier default_argument?
;
이 문법은 이미 modifiers* identifier
를 구문적으로 유효한 것으로 간주합니다.
노트
- 매개 변수 목록이 없는 람다에는 적용되지 않습니다.
ref x => x.ToString()
합법적이지 않을 것입니다. - 람다 매개 변수 목록은 여전히
implicit_anonymous_function_parameter
매개 변수와explicit_anonymous_function_parameter
매개 변수를 혼합할 수 없습니다. -
(ref readonly p) =>
,(scoped ref p) =>
및(scoped ref readonly p) =>
다음과 같이 명시적 매개 변수와 마찬가지로 허용됩니다.- C# 11의 저수준 구조체 개선
- C# 12에서
ref readonly
- 형식의 존재/부재는 한정자가 필요한지 선택적인지에 영향을 주지 않습니다. 즉, 형식이 있든 없든, 한정자가 필요한 경우에는 여전히 필요합니다. 마찬가지로, 수정자가 유형이 있는 경우 선택적이었다면, 유형이 없는 경우에도 선택적입니다.
의미론
https://learn.microsoft.com/dotnet/csharp/language-reference/language-specification/expressions#12192-anonymous-function-signatures 다음과 같이 업데이트됩니다.
lambda_parameter_list
내의 모든 lambda_parameter
요소는 type
를 가지고 있거나 type
를 가지고 있지 않아야 합니다. 전자는 "명시적으로 형식화된 매개 변수 목록"이며, 후자는 "암시적으로 형식화된 매개 변수 목록"입니다.
암시적으로 형식화된 매개 변수 목록의 매개 변수에는 default_argument
수 없습니다. 그들은 attribute_list
을 가질 수 있습니다.
익명 함수 변환에 대해 다음 변경이 필요합니다.
[...]
F에 명시적으로 또는 암시적으로 형식화된 매개 변수 목록있는 경우 D의 각 매개 변수는 매개 변수 한정자와 기본값을 무시하고 F의 해당 매개 변수와 동일한 형식 및 한정자를 줍니다.
참고/설명
scoped
및 params
명시적 형식이 없는 람다에서 명시적 한정자로 허용됩니다. 의미 체계는 둘 다 동일하게 유지됩니다. 특히, 둘 다 결정의 일부로서 내려진 것이 아닙니다.
무명 함수에 explicit_anonymous_function_signature 있는 경우 호환되는 대리자 형식 및 식 트리 형식 집합은 동일한 순서의 매개 변수 형식 및 한정자가 있는 형식으로 제한됩니다.
호환되는 대리자 형식을 제한하는 유일한 한정자는 ref
, out
, in
및 ref readonly
.
예를 들어 명시적으로 형식화된 람다에서는 현재 다음과 같이 모호합니다.
delegate void D<T>(scoped T t) where T : allows ref struct;
delegate void E<T>(T t) where T : allows ref struct;
class C
{
void M<T>() where T : allows ref struct
{
// error CS0121: The call is ambiguous between the following methods or properties: 'C.M1<T>(D<T>)' and 'C.M1<T>(E<T>)'
// despite the presence of the `scoped` keyword.
M1<T>((scoped T t) => { });
}
void M1<T>(D<T> d) where T : allows ref struct
{
}
void M1<T>(E<T> d) where T : allows ref struct
{
}
}
암시적으로 형식화된 람다를 사용하는 경우는 다음과 같습니다.
delegate void D<T>(scoped T t) where T : allows ref struct;
delegate void E<T>(T t) where T : allows ref struct;
class C
{
void M<T>() where T : allows ref struct
{
// This will remain ambiguous. 'scoped' will not be used to restrict the set of delegates.
M1<T>((scoped t) => { });
}
void M1<T>(D<T> d) where T : allows ref struct
{
}
void M1<T>(E<T> d) where T : allows ref struct
{
}
}
질문 열기
항상 C# 14의 람다에서 수정자로 사용되어야 하나요? 이 문제는 다음과 같은 경우에 중요합니다.
M((scoped s = default) => { });
이 경우, 'simple lambda'는 초기화자()를 포함할 수 없으므로 '단순 람다 매개 변수' 사양에
= default
해당하지 않습니다. 따라서 여기서scoped
는 C# 13에서처럼type
로 처리됩니다. 이를 유지 관리하시겠습니까? 아니면scoped
가 한정자로 항상 사용되므로, 잘못된 단순 매개 변수를 고려할 때도 여전히 한정자가 되도록 하는 더 간단한 규칙을 두는 것이 더 나을까요?추천 사항: 이것을 한정자로 만드세요. 우리는 이미 모든 소문자로 된 형식과 및 형식에서 사람들을 단념시키고 있으며, C#에서
scoped
라는 형식을 만드는 것을 불법으로 제정했습니다. 따라서 다른 라이브러리에서 형식을 참조하는 경우에 한정될 수 있습니다. 어떻게든 문제가 발생한 경우 해결 방법은 간단합니다.@scoped
사용하여 한정자가 아닌 형식 이름으로 만들기만 하면 됩니다.간단한 람다 매개 변수에서
params
허용하시겠습니까? 이전 람다 작업은 이미 람다 내params T[] values
에 대한 지원을 추가했습니다. 이 수식자는 선택 사항이며, 람다와 원래의 대리자는 이 수식자에 대해서 불일치가 있을 수 있습니다(람다에 수식자가 있지만 대리자에는 없는 경우 경고가 발생합니다). 간단한 람다 매개 변수를 사용하여 계속 허용해야 합니다. 예:M((params values) => { ... })
추천: 예. 허용합니다. 이 사양의 목적은 한정자를 유지하면서 람다 매개 변수에서 'type'을 삭제할 수 있도록 하는 것입니다. 이것은 또 다른 경우입니다. 또한 이러한 매개 변수에 대한 지원 특성처럼 임플에서 자연스럽게 발생하며, 이를 차단하려고 하는 것은 오히려 더 많은 작업이 필요합니다.
결론 1/15/2025: 아니요. 이 오류는 항상 오류입니다. 이에 대한 유용한 사례가 없는 것으로 보이며 아무도 이를 요구하지 않습니다. 처음부터 이 비감정적인 사례를 제한하는 것이 더 쉽고 안전합니다. 관련 사용 사례가 제시되면 다시 고려할 수 있습니다.
'스코핑'이 오버로드 결정에 영향을 주나요? 예를 들어, 대리자의 오버로드가 여러 개 있을 때, 하나는 '범위 지정' 매개 변수가 있는 반면 다른 것은 그렇지 않다면, '범위 지정'의 존재가 오버로드 결정에 영향을 미칠까요?
추천: 아니요. 범위 지정 오버로드 해결 기능이 없습니다. 이것은 명시적으로 타입이 지정된 람다에서 이미 작동하는 방식과 같습니다. 예를 들어:
delegate void D<T>(scoped T t) where T : allows ref struct; delegate void E<T>(T t) where T : allows ref struct; class C { void M<T>() where T : allows ref struct { M1<T>((scoped T t) => { }); } void M1<T>(D<T> d) where T : allows ref struct { } void M1<T>(E<T> d) where T : allows ref struct { } }
이것은 오늘날 모호합니다. 람다 매개 변수와
D<T>
에 '스코프'가 지정되어 있음에도 불구하고, 이 문제는 해결되지 않습니다. 암시적으로 형식화된 람다로 인해 변화가 있어야 한다고 믿지 않습니다.결론 2025년 1월 15일: 위의 내용은 '간단한 lambas'에도 사실로 적용됩니다. 'scoped'는 오버로드 해석에 영향을 미치지 않지만,
ref
및out
은 계속 오버로드 해석에 영향을 미칩니다.'(스코프 x) => ...' 람다를 허용하시겠습니까?
권장 사항: 예. 이를 허용하지 않으면 사용자가 명시적으로 형식화된 전체 람다를 작성할 수 있지만 암시적으로 형식화된 버전은 작성할 수 없는 시나리오에서 끝날 수 있습니다. 예를 들어:
delegate ReadOnlySpan<int> D(scoped ReadOnlySpan<int> x); class C { static void Main(string[] args) { D d = (scoped ReadOnlySpan<int> x) => throw null!; D d = (ReadOnlySpan<int> x) => throw null!; // error! 'scoped' is required } }
여기서 '범위'를 제거하면 오류가 발생합니다(이 경우 언어에는 람다와 대리자 간의 대응이 필요합니다.) 사용자가 형식을 명시적으로 지정하지 않고도 이와 같은 람다를 작성할 수 있기를 원하므로
(scoped x) => ...
허용되어야 합니다.결론 2025년 1월 15일:
(scoped x) => ...
람다를 허용합니다.
C# feature specifications