C#7.0の新しい実装を見ていて、ローカル関数が実装されているのは興味深いのですが、ローカル関数がラムダ式よりも優先されるシナリオと、その2つの違いは想像できません。
ラムダはanonymous
関数ですがローカル関数はそうでないことを理解していますが、ローカル関数がラムダ式よりも優れている実際のシナリオを理解できません
どんな例でも大歓迎です。ありがとう。
C#7.0の新しい実装を見ていて、ローカル関数が実装されているのは興味深いのですが、ローカル関数がラムダ式よりも優先されるシナリオと、その2つの違いは想像できません。
ラムダはanonymous
関数ですがローカル関数はそうでないことを理解していますが、ローカル関数がラムダ式よりも優れている実際のシナリオを理解できません
どんな例でも大歓迎です。ありがとう。
回答:
これは、ローカル関数が最初に議論されたC#設計会議ノートでMads Torgersenによって説明されました:
ヘルパー関数が必要です。単一の関数内からのみ使用しており、その関数のスコープ内にある変数と型パラメーターを使用している可能性があります。一方、ラムダとは異なり、ファーストクラスオブジェクトとして必要ないため、デリゲート型を指定して実際のデリゲートオブジェクトを割り当てる必要はありません。また、再帰的またはジェネリックにしたり、イテレータとして実装したりすることもできます。
さらに拡張すると、次のような利点があります。
パフォーマンス。
ラムダを作成するときは、デリゲートを作成する必要があります。これは、この場合は不要な割り当てです。ローカル関数は実際には単なる関数であり、デリゲートは必要ありません。
また、ローカル関数はローカル変数をキャプチャする方が効率的です。ラムダは通常、変数をクラスにキャプチャしますが、ローカル関数はstruct(を使用して渡されるref
)を使用できるため、割り当てを回避できます。
これは、ローカル関数の呼び出しが安価でインライン化できるため、パフォーマンスがさらに向上する可能性があることも意味します。
ローカル関数は再帰的です。
null
ラムダも再帰的にすることができますが、まずデリゲート変数に割り当て、次にラムダに割り当てる厄介なコードが必要です。ローカル関数は自然に再帰的です(相互再帰を含む)。
ローカル関数はジェネリックにすることができます。
ラムダは具象型の変数に割り当てる必要があるため、ジェネリックにすることはできません(その型は外部スコープのジェネリック変数を使用できますが、同じことではありません)。
ローカル関数はイテレータとして実装できます。
ラムダはyield return
(およびyield break
)キーワードを使用してIEnumerable<T>
-returning関数を実装することはできません。ローカル関数はできます。
ローカル関数がよく見えます。
これは上記の引用では言及されておらず、私の個人的な偏見かもしれませんが、デリゲート変数にラムダを割り当てるよりも、通常の関数構文の方が見栄えが良いと思います。ローカル関数もより簡潔です。
比較:
int add(int x, int y) => x + y;
Func<int, int, int> add = (x, y) => x + y;
Func<int, int, int> f = (x, y) => x + y; f(arg1:1, arg2:1);
。
svickのすばらしい答えに加えて、ローカル関数にはもう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;
}
#region Helpers
の下部に配置することに慣れているので、これは本当に便利です。そのため、その関数内での混乱を避け、特にメインクラスでの混乱を避けることができます。
ローカル関数をテストする方法についても疑問がある場合は、JustMockにそれを実行する機能があるため、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を担当する開発者の一人です。
.DoNothing().OccursOnce();
後でMock.Assert(foo);
メソッドを呼び出すことによって呼び出しが行われたことをアサートします。他のシナリオがどのようにサポートされているかに興味がある場合は、アサーティングオカレンスのヘルプ記事をご覧ください。
実行時間の長いメソッドを処理する場合は、インライン関数を使用して、ガベージコレクションのプレッシャーを特に回避します。特定のティッカーシンボルの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;
}
}
以下に示すように、利点を確認できます。ここでは、サンプル実装を確認できます。それが利点の説明に役立つことを願っています。