ラムダ式
ラムダ式の宣言
using System;
using System.Linq;
class Program
{
static void Main(string[] args)
{
Action a1 = () =>
{
Console.WriteLine("Action");
};
// Action
a1();
Action<int> a2 = i => Console.WriteLine(i);
// 2
a2(2);
Func<int> f1 = () => 1;
// 1
f1();
Func<string, string, int> f2 = (s1, s2) => int.Parse(s1 + s2);
// 54
f2.Invoke("5", "4");
// LINQ
// 9,1
new[] { 1, 3, 5 }
.Where(i => i <= 3)
.Select(i => i * i)
.OrderByDescending(i => i);
// イベント
Changed += (s, ea) => { Console.WriteLine("OnChanged"); };
}
static event EventHandler Changed;
}
|
ラムダ式はデリゲート型などを簡潔に記述するための文法である。ラムダ式を暗黙的な型宣言var
に代入することはできないが、指定された型に変換できるようにコンパイラーが判断する。基本的な記述方法は、(引数) => {式ブロック}
であり、引数が1つの場合は()
が省略でき、返り値の型は式ブロック内でreturn
しているインスタンスの型で判断する。
LINQのメソッド構文を記述するときやイベントの購読処理を記述するときによく利用される。
非同期処理(async/await)
using System;
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
// Mainメソッドはasyncにできない
// 非同期メソッドを同期的に待機する場合、GetAwaiter().GetResult() が使える
ExecuteAsync().GetAwaiter().GetResult();
}
static async Task ExecuteAsync()
{
var sw = Stopwatch.StartNew();
// 2秒待機
await TestAsync();
sw.Stop();
// await: 2 [sec]
Console.WriteLine($"await: {sw.Elapsed.Seconds} [sec]");
sw.Restart();
// この時点では待機しない
var task = TestAsync();
// 未await: 0 [sec]
Console.WriteLine($"未await: {sw.Elapsed.Seconds} [sec]");
await task;
// await: 2 [sec]
Console.WriteLine($"await: {sw.Elapsed.Seconds} [sec]");
sw.Restart();
// 一度完了したTaskをawaitすると、すぐに完了する
await task;
// 再await: 0 [sec]
Console.WriteLine($"再await: {sw.Elapsed.Seconds} [sec]");
var task2 = TestAsync();
sw.Restart();
sw.Restart();
// async voidメソッドを呼んでも待機しない
VoidAsync();
// async void: 0 [sec]
Console.WriteLine($"async void: {sw.Elapsed.Seconds} [sec]");
sw.Restart();
var r1 = await TestAsync2();
// await Task<int>: Result=1 2 [sec]
Console.WriteLine($"await Task<int>: Result={r1} {sw.Elapsed.Seconds} [sec]");
var t2 = TestAsync2();
sw.Restart();
// Task<T> のResultプロパティで結果を取得できるが、プロパティアクセスで待機することになる
var r2 = t2.Result;
// Get Task<int>.Result: Result=1 2 [sec]
Console.WriteLine($"Get Task<int>.Result: Result={r2} {sw.Elapsed.Seconds} [sec]");
sw.Restart();
// await同様、一度完了したTaskのResultプロパティは待機せずに取得できる
var r3 = t2.Result;
// Get Task<int>.Result again: Result=1 0 [sec]
Console.WriteLine($"Get Task<int>.Result again: Result={r3} {sw.Elapsed.Seconds} [sec]");
}
static async Task TestAsync()
{
await Task.Delay(TimeSpan.FromSeconds(2));
}
static async void VoidAsync()
{
await Task.Delay(TimeSpan.FromSeconds(2));
}
static async Task<int> TestAsync2()
{
await Task.Delay(TimeSpan.FromSeconds(2));
return 1;
}
}
|
C#ではasync
修飾子を持つ関数を非同期関数と呼び、非同期という用語はこのasync
を扱う説明に使っている。C#の非同期プログラミングはasync
のみならず、APIも利用できるが、この記事ではasync
/await
の使い方を中心とした説明にとどめる。
非同期関数は返り値がTask
型およびそのジェネリクス型のTask<TResult>
(いずれもSystem.Threading.Tasks
名前空間)、もしくはvoid
である必要がある。Task
/Task<TResult>
型を返り値とする非同期関数は、await
式により結果の取得を待機できる。サンプルコードでは非同期に2秒時間がかかる処理をしているが、await
することで呼び出し側が2秒待機していることが分かる。
await Task
はvoid
に相当し返り値を持たず、await Task<TResult>
はTResult
型に相当する返り値を持つ。非同期関数をawait
しない場合は、待機せず、その返り値であるタスクをawait
する、もしくはResult
プロパティにアクセスしようとした時点で待機する。一度待機して完了したタスクは、再度結果を参照する時には待機しない(=再実行されない)。
async void
な非同期関数は待機できない。主にイベントハンドラーの購読に登録する場合に利用することが多いが、例外がスローされた場合の処理が複雑になりがちであり、注意が必要である。
■
以上でC#の主要な文法を一通り説明した。文法を羅列するだけでもかなりの項目数だったが、本稿では実利用者目線でできるだけコンパクトにまとまるように努力した。ぜひ、日々のコーディングの「あれ、どう書くんだっけ?」という場面で役立てていただけるとうれしい。
- 前のページへ
- 次のページへ
- 【記事のページ一覧】
- 1 プログラムの実行と制御/型と変数
- 2 演算子/ステートメント
- 3 名前空間/クラス
- 4 メソッド/プロパティ/イベント/インデクサー/演算子オーバーロード/コンストラクターコンストラクタとデストラクターデストラクタ
- 5 構造体/継承とインターフェース/列挙型/イテレーター/例外処理/リソースの解放
- 6 ラムダ式/非同期処理(async/await)