로컬 함수 vs Lambda C # 7.0


178

C # 7.0 의 새로운 구현 을보고 있는데 로컬 함수를 구현했다는 것이 흥미 롭지 만 람다 식보다 로컬 함수가 선호되는 시나리오와 두 가지의 차이점은 무엇인지 상상할 수 없습니다.

람다는 anonymous함수이지만 로컬 함수는 아니지만 함수는 람다 식보다 이점이있는 실제 시나리오를 파악할 수 없습니다.

어떤 예라도 대단히 감사하겠습니다. 감사.


9
람다를 null로 초기화하지 않고 제네릭, 출력 매개 변수, 재귀 함수 등
Kirk Woll

5
@KirkWoll-답변으로 게시해야합니다.
Enigmativity

답변:


276

이것은 C # 디자인 미팅 노트에서 Mads Torgersen이 로컬 기능을 처음 논의한 곳에서 설명했습니다 .

도우미 기능이 필요합니다. 단일 함수 내에서만 사용하고 있으며 포함하는 함수의 범위에 속하는 변수 및 유형 매개 변수를 사용합니다. 반면에 람다와는 달리 람다와는 일등 객체가 필요하지 않으므로 델리게이트 유형을 지정하고 실제 델리게이트 객체를 할당 할 필요가 없습니다. 또한 재귀 적이거나 일반적인 것이거나 반복자로 구현하기를 원할 수 있습니다.

더 확장하면 장점은 다음과 같습니다.

  1. 공연.

    람다를 만들 때는 델리게이트를 만들어야하는데,이 경우 불필요한 할당입니다. 로컬 함수는 실제로 함수일 뿐이며 델리게이트가 필요하지 않습니다.

    또한 로컬 함수는 로컬 변수를 캡처 할 때보다 효율적입니다. 람다는 일반적으로 변수를 클래스로 캡처하는 반면 로컬 함수는을 사용하여 전달되는 struct를 사용하여 ref할당을 다시 피할 수 있습니다.

    또한 로컬 함수 호출이 저렴하고 인라인 될 수 있으므로 성능이 더욱 향상 될 수 있습니다.

  2. 로컬 함수는 재귀적일 수 있습니다.

    람다는 재귀 적 일 수도 있지만 null, 대리자 변수에 할당 한 다음 람다에 할당 하는 어색한 코드가 필요합니다 . 로컬 함수는 자연스럽게 재귀적일 수 있습니다 (상호 재귀 적 포함).

  3. 로컬 기능은 일반적 일 수 있습니다.

    람다는 구체적인 유형의 변수에 할당해야하기 때문에 일반이 될 수 없습니다 (해당 유형은 외부 범위의 일반 변수를 사용할 수 있지만 동일하지는 않습니다).

  4. 로컬 함수는 반복자로 구현할 수 있습니다.

    Lambdas 는 yield return(and yield break) 키워드를 사용하여 IEnumerable<T>반환 함수 를 구현할 수 없습니다 . 지역 기능이 가능합니다.

  5. 로컬 기능이 더 좋아 보입니다.

    이것은 위의 인용문에서 언급되지 않았으며 개인적인 편견 일 수도 있지만 일반적인 함수 구문은 람다를 대리자 변수에 할당하는 것보다 낫다고 생각합니다. 지역 기능도 간결합니다.

    비교:

    int add(int x, int y) => x + y;
    Func<int, int, int> add = (x, y) => x + y;
    

22
로컬 함수에는 호출자 측에 매개 변수 이름이 있음을 추가하고 싶습니다. 람다는 그렇지 않습니다.
Lensflare

3
@Lensflare 람다의 매개 변수 이름은 보존되지 않지만, 이는 자신의 이름을 가진 델리게이트로 변환되어야하기 때문입니다. 예를 들면 다음과 같습니다 Func<int, int, int> f = (x, y) => x + y; f(arg1:1, arg2:1);..
svick

1
훌륭한 목록! 그러나 IL / JIT 컴파일러가 1에서 언급 한 모든 최적화를 어떻게 수행 할 수 있는지 상상할 수 있습니다.
Marcin Kaczmarek

1
@Casebash 람다는 항상 대리자를 사용하고 해당 대리자는 클로저를로 유지하기 때문에 object. 따라서 람다는 구조체를 사용할 수 있지만 상자에 넣어야하므로 추가 할당이 필요합니다.
svick

1
@happybits 주로 메소드에 전달할 때와 같이 이름을 지정할 필요가 없을 때.
svick

83

svick의 큰 대답 외에도 로컬 함수 에는 또 다른 이점이
있습니다 return. 명령문 후에도 함수의 어느 곳에서나 정의 할 수 있습니다 .

public double DoMath(double a, double b)
{
    var resultA = f(a);
    var resultB = f(b);
    return resultA + resultB;

    double f(double x) => 5 * x + 3;
}

5
이것은 모든 도우미 함수를 함수 #region Helpers의 맨 아래에 배치하는 데 익숙해 질 수 있으므로 해당 함수 내에서 혼란을 피하고 주 클래스에서 혼란을 피할 수 있기 때문에 정말 유용합니다 .
AustinWBryan

나는 또한 이것에 감사한다. 시작 위치를 찾기 위해 둘러 볼 필요가 없기 때문에보고있는 주요 기능을보다 쉽게 ​​읽을 수 있습니다. 구현 세부 사항을 보려면 끝 부분을 계속 살펴보십시오.
Remi Despres-Smyth

3
함수가 너무 크면 영역이 필요하고 너무 큽니다.
ssmith 2009

9

로컬 기능을 테스트하는 방법이 궁금하다면 JustMock에 기능이 있는지 확인 해야합니다. 테스트 할 간단한 클래스 예제는 다음과 같습니다.

public class Foo // the class under test
{ 
    public int GetResult() 
    { 
        return 100 + GetLocal(); 
        int GetLocal () 
        { 
            return 42; 
        } 
    } 
}

다음은 테스트 모습입니다.

[TestClass] 
public class MockLocalFunctions 
{ 
    [TestMethod] 
    public void BasicUsage() 
    { 
        //Arrange 
        var foo = Mock.Create<Foo>(Behavior.CallOriginal); 
        Mock.Local.Function.Arrange<int>(foo, "GetResult", "GetLocal").DoNothing(); 

        //Act 
        var result = foo. GetResult(); 

        //Assert 
        Assert.AreEqual(100, result); 
    } 
} 

다음은 JustMock 설명서에 대한 링크 입니다.

부인 성명. 저는 JustMock을 담당하는 개발자 중 한 사람입니다 .


사람들이 자신의 도구를 사용하도록 촉구하는 열정적 인 개발자를 보는 것이 좋습니다. 정규직으로 개발자 도구를 작성하는 데 어떻게 관심이 있었습니까? 미국인으로서 저의 인상은 석사 나 박사 학위가 없다면 그러한 직업을 찾기가 어렵다는 것입니다. comp sci.
존 자브로 스키

존 안녕하세요, 친절한 말에 감사드립니다. 소프트웨어 개발자로서 고객에게 제공하는 가치에 대해 고객이 높이 평가하는 것보다 더 좋은 점은 없습니다. 도전적이고 경쟁적인 작업에 대한 열망과 결합하면 내가 열정적으로 할 것들의 목록이 상당히 제한됩니다. 생산성 개발자 툴링 작성이 그 목록에 있습니다. 적어도 내 마음에는 :) 경력과 관련하여 개발자 도구를 제공하는 회사는 모든 소프트웨어 회사의 비율이 매우 적기 때문에 그러한 기회를 찾기가 더 어렵다고 생각합니다.
Mihail Vladov

별도의 질문입니다. 여기서 VerifyAll에 전화하지 않겠습니까? JustMock에게 로컬 함수가 호출되었는지 확인하도록 지시하는 방법이 있습니까?
John Zabroski

2
안녕하세요 @JohnZabroski, 테스트 시나리오는 어설 션 발생이 필요하지 않았습니다. 물론 전화가 걸 렸는지 확인할 수 있습니다. 먼저 메소드 호출 횟수를 지정해야합니다. 이렇게 : .DoNothing().OccursOnce();나중에 Mock.Assert(foo);메소드 를 호출하여 호출했다고 주장합니다 . 다른 시나리오가 어떻게 지원되는지 관심이 있으시면 Asserting Occurrence 도움말 기사를 읽으십시오 .
Mihail Vladov

0

더 긴 실행 방법을 다룰 때 가비지 수집 압력을 피하기 위해 인라인 함수를 사용합니다. 주어진 시세 기호에 대해 2 년 또는 시장 데이터를 얻고 싶다고 가정하십시오. 또한 필요한 경우 많은 기능과 비즈니스 로직을 포장 할 수 있습니다.

하나는 서버에 대한 소켓 연결을 열고 이벤트를 이벤트에 바인딩하는 데이터를 반복하는 것입니다. 클래스가 설계된 것과 같은 방식으로 생각할 수 있습니다. 단 하나의 기능에 대해서만 작동하는 헬퍼 메소드를 작성하는 것은 아닙니다. 아래는 이것이 어떻게 보일지에 대한 샘플입니다. 변수를 사용하고 있으며 "helper"메서드가 마지막에 있습니다. 마지막으로 Exchange 클래스가 외부 / 주입 된 경우 이벤트 처리기를 멋지게 제거합니다. 보류중인 이벤트 처리기가 등록되지 않았습니다.

void List<HistoricalData> RequestData(Ticker ticker, TimeSpan timeout)
{
    var socket= new Exchange(ticker);
    bool done=false;
    socket.OnData += _onData;
    socket.OnDone += _onDone;
    var request= NextRequestNr();
    var result = new List<HistoricalData>();
    var start= DateTime.Now;
    socket.RequestHistoricalData(requestId:request:days:1);
    try
    {
      while(!done)
      {   //stop when take to long….
        if((DateTime.Now-start)>timeout)
           break;
      }
      return result;

    }finally
    {
        socket.OnData-=_onData;
        socket.OnDone-= _onDone;
    }


   void _OnData(object sender, HistoricalData data)
   {
       _result.Add(data);
   }
   void _onDone(object sender, EndEventArgs args)
   {
      if(args.ReqId==request )
         done=true;
   } 
}

아래에 언급 된 바와 같은 장점을 볼 수 있습니다. 여기서 샘플 구현을 볼 수 있습니다. 이점을 설명하는 데 도움이되기를 바랍니다.


2
1. 이것은 로컬 함수를 보여주기위한 정말 복잡한 예제와 설명입니다. 2. 로컬 함수는이 예제에서 람다와 비교할 때 할당을 피하지 않습니다. 여전히 델리게이트로 변환되어야하기 때문입니다. 그래서 그들이 어떻게 GC를 피할 수 있을지 모르겠습니다.
svick

1
svick의 대답은 변수를 전달 / 복사하지 않고 나머지를 실제로 잘 다룹니다. 그의 대답 복제 할 필요가 없습니다
월터 Vehoeven
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.