web-dev-qa-db-ja.com

ローカル関数とLambda C#7.0

C#7.0 の新しい実装を見ていますが、ローカル関数を実装しているのは面白いと思いますが、想像できませんローカル関数がラムダ式よりも優先されるシナリオと、2つの違いは何ですか。

ラムダはanonymous関数であり、ローカル関数はそうではないことを理解していますが、ローカル関数がラムダ式よりも有利である現実のシナリオを理解することはできません

どんな例でも大歓迎です。ありがとう。

151
Sid

これは、ローカル機能が最初に議論されたC#Design Meeting NotesでMads Torgersenによって説明されました

ヘルパー関数が必要です。単一の関数内からのみ使用し、その関数を含むスコープ内の変数と型パラメーターを使用する可能性があります。一方、ラムダとは異なり、ラムダをファーストクラスオブジェクトとして必要としないため、ラムダにデリゲート型を指定して実際のデリゲートオブジェクトを割り当てる必要はありません。また、再帰的または汎用にするか、反復子として実装することもできます。

さらに拡張するには、次の利点があります。

  1. パフォーマンス。

    ラムダを作成する場合、デリゲートを作成する必要があります。この場合、これは不要な割り当てです。ローカル関数は実際には単なる関数であり、デリゲートは必要ありません。

    また、ローカル関数はローカル変数をキャプチャする方が効率的です。ラムダは通常、変数をクラスにキャプチャしますが、ローカル関数は構造体(refを使用して渡されます)を使用できます。

    これは、ローカル関数の呼び出しがより安価でインライン化できることを意味し、パフォーマンスがさらに向上する可能性があります。

  2. ローカル関数は再帰的です。

    ラムダも再帰的ですが、最初にnullをデリゲート変数に割り当ててからラムダを割り当てるという厄介なコードが必要です。ローカル関数は自然に再帰的(相互再帰的を含む)にすることができます。

  3. ローカル関数はジェネリックにすることができます。

    ラムダは、具象型の変数に割り当てる必要があるため、ジェネリックにすることはできません(その型は外部スコープからジェネリック変数を使用できますが、それは同じではありません)。

  4. ローカル関数は反復子として実装できます。

    Lambdasはyield return(およびyield break)キーワードを使用してIEnumerable<T>- returning関数を実装できません。ローカル機能ができます。

  5. ローカル関数の見た目が良くなりました。

    これは上記の引用では言及されておらず、個人的な偏見に過ぎないかもしれませんが、デリゲート変数にラムダを割り当てるよりも、通常の関数構文の方が良いと思います。ローカル関数もより簡潔です。

    比較:

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

svick's great answer に加えて、ローカル関数にはもう1つの利点があります。
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;
}
72
Tim Pohlmann

また、ローカル機能をテストする方法を知りたい場合は、 JustMock をチェックする必要があります。テストする簡単なクラスの例を次に示します。

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

そして、テストは次のようになります。

[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); 
    } 
} 
 </ code>

JustMock documentation へのリンクです。

免責事項。私は JustMock を担当する開発者の一人です。

4
Mihail Vladov

インライン関数を使用して、実行時間の長いメソッドを扱う際にガベージコレクションの圧力を特別に回避します。特定のティッカーシンボルの2年または市場データを取得したいとします。また、必要に応じて多くの機能とビジネスロジックを詰め込むことができます。

1つは、サーバーへのソケット接続を開き、データをループ処理してイベントをイベントにバインドすることです。クラスの設計と同じように考えることができます。1つの機能だけで実際に機能するヘルパーメソッドを、あちこちで書いているのは1つだけです。以下は、これがどのように見えるかのサンプルです。変数を使用しており、「ヘルパー」メソッドが最後にあることに注意してください。最後に、イベントハンドラーをうまく削除します。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;
   } 
}

以下に記載されているように利点を見ることができます、ここではサンプル実装を見ることができます。それが利点の説明に役立つことを願っています。