다음을 통해 공유


한정자를 사용하여 간단한 람다 매개 변수

비고

이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 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를 구문적으로 유효한 것으로 간주합니다.

노트

  1. 매개 변수 목록이 없는 람다에는 적용되지 않습니다. ref x => x.ToString() 합법적이지 않을 것입니다.
  2. 람다 매개 변수 목록은 여전히 implicit_anonymous_function_parameter 매개 변수와 explicit_anonymous_function_parameter 매개 변수를 혼합할 수 없습니다.
  3. (ref readonly p) =>, (scoped ref p) =>(scoped ref readonly p) => 다음과 같이 명시적 매개 변수와 마찬가지로 허용됩니다.
  4. 형식의 존재/부재는 한정자가 필요한지 선택적인지에 영향을 주지 않습니다. 즉, 형식이 있든 없든, 한정자가 필요한 경우에는 여전히 필요합니다. 마찬가지로, 수정자가 유형이 있는 경우 선택적이었다면, 유형이 없는 경우에도 선택적입니다.

의미론

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의 해당 매개 변수와 동일한 형식 및 한정자를 줍니다.

참고/설명

scopedparams 명시적 형식이 없는 람다에서 명시적 한정자로 허용됩니다. 의미 체계는 둘 다 동일하게 유지됩니다. 특히, 둘 다 결정의 일부로서 내려진 것이 아닙니다.

무명 함수에 explicit_anonymous_function_signature 있는 경우 호환되는 대리자 형식 및 식 트리 형식 집합은 동일한 순서의 매개 변수 형식 및 한정자가 있는 형식으로 제한됩니다.

호환되는 대리자 형식을 제한하는 유일한 한정자는 ref, out, inref 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
    {
    }
}

질문 열기

  1. 항상 C# 14의 람다에서 수정자로 사용되어야 하나요? 이 문제는 다음과 같은 경우에 중요합니다.

    M((scoped s = default) => { });
    

    이 경우, 'simple lambda'는 초기화자()를 포함할 수 없으므로 '단순 람다 매개 변수' 사양에 = default 해당하지 않습니다. 따라서 여기서 scoped는 C# 13에서처럼 type로 처리됩니다. 이를 유지 관리하시겠습니까? 아니면 scoped가 한정자로 항상 사용되므로, 잘못된 단순 매개 변수를 고려할 때도 여전히 한정자가 되도록 하는 더 간단한 규칙을 두는 것이 더 나을까요?

    추천 사항: 이것을 한정자로 만드세요. 우리는 이미 모든 소문자로 된 형식과 형식에서 사람들을 단념시키고 있으며, C#에서 scoped라는 형식을 만드는 것을 불법으로 제정했습니다. 따라서 다른 라이브러리에서 형식을 참조하는 경우에 한정될 수 있습니다. 어떻게든 문제가 발생한 경우 해결 방법은 간단합니다. @scoped 사용하여 한정자가 아닌 형식 이름으로 만들기만 하면 됩니다.

  2. 간단한 람다 매개 변수에서 params 허용하시겠습니까? 이전 람다 작업은 이미 람다 내 params T[] values에 대한 지원을 추가했습니다. 이 수식자는 선택 사항이며, 람다와 원래의 대리자는 이 수식자에 대해서 불일치가 있을 수 있습니다(람다에 수식자가 있지만 대리자에는 없는 경우 경고가 발생합니다). 간단한 람다 매개 변수를 사용하여 계속 허용해야 합니다. 예: M((params values) => { ... })

    추천: 예. 허용합니다. 이 사양의 목적은 한정자를 유지하면서 람다 매개 변수에서 'type'을 삭제할 수 있도록 하는 것입니다. 이것은 또 다른 경우입니다. 또한 이러한 매개 변수에 대한 지원 특성처럼 임플에서 자연스럽게 발생하며, 이를 차단하려고 하는 것은 오히려 더 많은 작업이 필요합니다.

    결론 1/15/2025: 아니요. 이 오류는 항상 오류입니다. 이에 대한 유용한 사례가 없는 것으로 보이며 아무도 이를 요구하지 않습니다. 처음부터 이 비감정적인 사례를 제한하는 것이 더 쉽고 안전합니다. 관련 사용 사례가 제시되면 다시 고려할 수 있습니다.

  3. '스코핑'이 오버로드 결정에 영향을 주나요? 예를 들어, 대리자의 오버로드가 여러 개 있을 때, 하나는 '범위 지정' 매개 변수가 있는 반면 다른 것은 그렇지 않다면, '범위 지정'의 존재가 오버로드 결정에 영향을 미칠까요?

    추천: 아니요. 범위 지정 오버로드 해결 기능이 없습니다. 이것은 명시적으로 타입이 지정된 람다에서 이미 작동하는 방식과 같습니다. 예를 들어:

    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'는 오버로드 해석에 영향을 미치지 않지만, refout은 계속 오버로드 해석에 영향을 미칩니다.

  4. '(스코프 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) => ... 람다를 허용합니다.