이상한 반환 구문 문


106

나는 이것이 이상하게 들릴 수 있지만 인터넷 에서이 구문을 검색하는 방법도 모르고 정확히 무엇을 의미하는지 확실하지 않습니다.

그래서 몇 가지 MoreLINQ 코드를 살펴본 후이 방법을 발견했습니다.

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));

    return _(); IEnumerable<TSource> _()
    {
        var knownKeys = new HashSet<TKey>(comparer);
        foreach (var element in source)
        {
            if (knownKeys.Add(keySelector(element)))
                yield return element;
        }
    }
}

이 이상한 반환 진술은 무엇입니까? return _();?


6
아니면 다음을 의미 return _(); IEnumerable<TSource> _()합니까?
Alex K.

6
@ Steve, OP가 더 많은 것을 참조하고 있는지 궁금 return _(); IEnumerable<TSource> _()합니다 yield return.
Rob

5
나는 그가이 대사를 의미했다고 생각한다 return _(); IEnumerable<TSource> _(). 실제 return 문이 아닌 모양으로 혼동 될 수 있습니다.
Mateusz

5
@AkashKava OP는 이상한 반환 진술이 있다고 말했습니다. 불행히도 코드에는 두 개의 return 문이 있습니다. 따라서 사람들이 자신이 언급하는 내용에 대해 혼란스러워해도 이해할 수 있습니다.
mjwills jul.

5
질문을 수정했으며 다시 한 번 혼란을 드려 죄송합니다.
kuskmen

답변:


106

이것은 로컬 기능을 지원하는 C # 7.0입니다 ....

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
       this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new 
           ArgumentNullException(nameof(source));
        if (keySelector == null) throw 
             new ArgumentNullException(nameof(keySelector));

        // This is basically executing _LocalFunction()
        return _LocalFunction(); 

        // This is a new inline method, 
        // return within this is only within scope of
        // this method
        IEnumerable<TSource> _LocalFunction()
        {
            var knownKeys = new HashSet<TKey>(comparer);
            foreach (var element in source)
            {
                if (knownKeys.Add(keySelector(element)))
                    yield return element;
            }
        }
    }

현재 C # Func<T>

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
       this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new 
           ArgumentNullException(nameof(source));
        if (keySelector == null) throw 
             new ArgumentNullException(nameof(keySelector));

        Func<IEnumerable<TSource>> func = () => {
            var knownKeys = new HashSet<TKey>(comparer);
            foreach (var element in source)
            {
                if (knownKeys.Add(keySelector(element)))
                    yield return element;
            }
       };

        // This is basically executing func
        return func(); 

    }

트릭은 _ ()가 사용 된 후에 선언된다는 것입니다.

로컬 기능의 실제 사용

위의 예는 인라인 메서드를 사용할 수있는 방법에 대한 데모 일 뿐이지 만 메서드를 한 번만 호출하려는 경우에는 쓸모가 없습니다.

그러나 위의 예에서 PhoshiLuaan의 주석에서 언급했듯이 로컬 함수를 사용하는 이점이 있습니다. yield return이있는 함수는 누군가가 반복하지 않으면 실행되지 않으므로이 경우에는 아무도 값을 반복하지 않더라도 로컬 함수 외부의 메서드가 실행되고 매개 변수 유효성 검사가 수행됩니다.

메서드에서 여러 번 코드를 반복했습니다.이 예제를 살펴 보겠습니다.

  public void ValidateCustomer(Customer customer){

      if( string.IsNullOrEmpty( customer.FirstName )){
           string error = "Firstname cannot be empty";
           customer.ValidationErrors.Add(error);
           ErrorLogger.Log(error);
           throw new ValidationError(error);
      }

      if( string.IsNullOrEmpty( customer.LastName )){
           string error = "Lastname cannot be empty";
           customer.ValidationErrors.Add(error);
           ErrorLogger.Log(error);
           throw new ValidationError(error);
      }

      ... on  and on... 
  }

나는 이것을 다음과 같이 최적화 할 수있다.

  public void ValidateCustomer(Customer customer){

      void _validate(string value, string error){
           if(!string.IsNullOrWhitespace(value)){

              // i can easily reference customer here
              customer.ValidationErrors.Add(error);

              ErrorLogger.Log(error);
              throw new ValidationError(error);                   
           }
      }

      _validate(customer.FirstName, "Firstname cannot be empty");
      _validate(customer.LastName, "Lastname cannot be empty");
      ... on  and on... 
  }

4
@ZoharPeled 음 .. 게시 된 코드가 수행 하는 기능에 대한 사용을 보여 .. :)

2
@ColinM의 장점 중 하나는 익명 함수가 '호스트'에서 변수에 쉽게 액세스 할 수 있다는 것입니다.
mjwills jul.

6
C #에서 이것이 실제로 익명 함수라고 불리는 것이 확실합니까? 즉, _AnonymousFunction또는 그냥 라는 이름이있는 것처럼 보이지만 _진정한 익명 함수는 (x,y) => x+y. 이것을 로컬 함수라고 부르지 만 C # 용어에 익숙하지 않습니다.
chi

12
명확하게 말하면 아무도 지적하지 않은 것처럼 보이므로이 코드 조각은 반복기 이기 때문에 로컬 함수를 사용 하고 있으므로 느리게 실행됩니다. 로컬 함수가 없으면 처음 사용할 때 입력 유효성 검사가 발생한다는 것을 받아들이거나 거의 이유없이 다른 하나의 메서드에 의해서만 호출되는 메서드를 가져야합니다.
Phoshi

6
@ColinM 게시 된 예제 kuksmen은 실제로 이것이 최종적으로 구현 된 주된 이유 중 하나입니다.를 사용하여 함수를 만들면 yield return열거 형이 실제로 열거 될 때까지 코드가 실행되지 않습니다. 예를 들어 즉시 인수를 확인하고 싶기 때문에 이는 바람직하지 않습니다. C #에서이 작업을 수행하는 유일한 방법은 메서드를 yield returns가있는 메서드와없는 메서드의 두 가지 메서드로 분리하는 것 입니다. 인라인 메서드를 사용하면 내부에yield using 메서드 를 선언 할 수 있으므로 부모의 내부에 있고 재사용 할 수없는 메서드의 혼란과 잠재적 인 오용을 방지 할 수 있습니다 .
Luaan

24

더 간단한 예를 고려하십시오.

void Main()
{
    Console.WriteLine(Foo()); // Prints 5
}

public static int Foo()
{
    return _();

    // declare the body of _()
    int _()
    {
        return 5;
    }
}

_() return 문을 포함하는 메서드 내에서 선언 된 로컬 함수입니다.


3
예, 저는 지역 함수에 대해 알고 있습니다. 그것이 저를 속인 포맷이었습니다. 이것이 표준이되지 않기를 바랍니다.
kuskmen

20
같은 줄에서 시작하는 함수 선언을 의미합니까? 그렇다면 동의합니다. 끔찍합니다!
Stuart

3
네, 그게 제가 의미하는 바입니다.
kuskmen

9
그 이름을 제외하고는 밑줄도 끔찍합니다
Icepickle

1
@AkashKava : 문제는 그것이 합법적 인 C #인지 아닌지가 아니라 코드가 이와 같이 형식화되었을 때 이해하기 쉬운 지 (따라서 유지 관리하기 쉽고 읽기에도 즐거운 지) 여부입니다. 개인적인 취향이 중요한 역할을하지만 저는 스튜어트의 의견에 동의하는 경향이 있습니다.
PJTraill
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.