プログラミング - TECH PLAY - TECH PLAY

TECH PLAY

プログラミング

イベント

マガジン

技術ブログ

はじめに 2026年に開催された DroidKaigi 2026 に、弊社の開発本部から 3 名のエンジニアが参加してきましたので、イベントの様子や印象に残ったセッションをご紹介します。 イベントの様子 スポンサーブース エブリーは今回、ゴールドスポンサーとしてブースを出展させていただきました! 足を運んでいただいた皆様、本当にありがとうございました! ブース企画 アンケートボード ブースでは、「AI時代!どこまで越境したいですか?」をテーマにした参加型のアンケートボードを実施しました! Android開発をベースにしつつ、バックエンドやPdM、データサイエンスといった他の領域へどのようにスキルを広げていきたいか、皆様のリアルな声を聞かせていただきました! 回答いただいた多くの皆様、ありがとうございました!最終結果はこちらです……! 2日間でいただいたシールは、合計およそ 400 枚。領域ごとの内訳は次のようになりました。 最も票が集まったのは「Android」でしたが、Backend と iOS がほぼ同数で並び、この3つが上位を分け合う形になりました。一方で全体を見ると、Android 以外の領域に貼られたシールは全体の 8 割弱。「Android を軸に据えつつ、その外側にも手を伸ばしていきたい」という方が多数派でした。 また、シールを貼っていただきながら、こんな声も聞かせていただきました。 モバイル領域が好きなので、クロスプラットフォームでやっていきたい コードは AI が書いてくれるので、プロダクトをどうグロースさせるかを考えられるようになりたい 「AI 時代にどう越境するか」という問いに対して、技術の横方向に広げていく方向と、プロダクトづくりそのものへ踏み込んでいく方向、その両方のリアルな声を伺うことができました。 ※シール数は写真からの集計のため、概算値です。 Xフォロー&くじ引き エブリー開発部の X アカウントをフォローいただくと、くじを1回引けるという企画も実施しました。 景品は、レンジ調理鍋・まな板・計量スプーン・しゃもじ・お箸など、普段の料理で使えるキッチングッズです。 ハズレの方にも、CTO 自らがテイスティングして選んだ「CTO ブレンド」のコーヒーをお渡ししていたので、くじを引いてくださった方には全員何かしらお持ち帰りいただけるようにしていました。 キッチングッズが当たった方に喜んでいただけて、こちらも嬉しかったです! またXをフォローいただいた皆様、本当にありがとうございました!X ではテックブログの更新情報も発信しているので、ぜひチェックしていただけると幸いです! ネイル体験会 会場ではプロのネイリストによるネイル体験会が開催されており体験してきました! 流れとしては、ネイルをする指を2本選び、それぞれのデザインを決めていくというもの。ベースカラーはネイリストの方と相談しながら決められるので、ネイルに詳しくなくても安心して選ぶことができました。 デザインは、DroidKaigi のキャラクター3種類とロゴの中から好きなものをチョイスできました。指先に DroidKaigi のキャラクターがいてくれるので、ふとした拍子に目に入るたび嬉しくなります。 他社のスポンサーブース REALITY さん REALITY さんは、AEP 対応に関するアンケートを行っていました! AEP (Apps Experience Program) は、Google が指定した要件を満たすと認定を受けられ、Google Play の新しい料金表の適用などの特典が得られるプログラムです。 Material3 は対応済み (80% 以上) の回答が多く、予想以上でした。一方でフルコンポーズ化は、まだ対応中・検討中という回答の方が多いようでした。 アーキテクチャも公開されていました。3D アバター以外の箇所はネイティブで作成されているとのことで、Unity を使っていると思っていたので驚きました。 BIZREACH さん BIZREACH さんは、AI が進化して楽になったことについてアンケートを行っていました! テストコードの生成やエラーの原因調査のような、コーディング業務の補佐的な立ち位置に留まらず、相談相手として活用している方が多く面白かったです。 晩ご飯の献立については、他と比べると少ないようでした。 デリッシュキッチン の出番のようです! エムスリー さん エムスリーさんは、毎年恒例の、プログラムのコードが印刷されたクリアファイルを配布されていました。 なんと去年よりコードが短くなっているとのことでした! クリアファイルの詳細については、昨年版のものになりますが エムスリーさんのテックブログ で公開されていますので、ぜひご覧ください! セッション紹介 なんとかする力 〜Androidエンジニアからマネージャー、さらにその先へ?〜 発表者: m.coder さん(フラー株式会社) レポート: 岡田 m.coder さんに、「目の前の課題を『なんとかする』の積み重ねが今の自分を作ってきた」という考えをもとに、キャリアとの向き合い方を語っていただいたセッションでした。 仕事のやりやすさは「何を・いつまでに・どこまでやるか」が決まっているかで大きく変わり、曖昧な箇所を明確にして不確実性を下げること自体が価値ある仕事だという話から始まりました。 印象的だったのは、役職が上がっていくにつれ、皆等しく抽象度の高い課題の解決を求められるというお話です。「なんとなくチームの雰囲気が悪い」「なんかプロジェクトの品質が悪い気がする」といった、課題かどうかすら曖昧なものを扱う必要があるという具体例に痛く納得しました。こういった漠然とした課題については、どうしても目を瞑りがちなので、自身のマインドセットを見直す必要があるなと痛感しました。 またテックリードとマネージャーは向き合い方が違うだけで、どちらも「自分以外の領域(チームや組織)をなんとかする」役割だという整理も面白かったです。 自分のキャリアを考えるうえで、抽象度の高い問題に立ち向かうべきという方針や、それを実現させる方法について非常に学びになりました。ご自身の経験から語られている箇所も多く、熱いメッセージをいただいた気持ちになりました。 また冒頭で『エンジニアリング組織論への招待』を紹介していただきました。1 章だけでも読む価値があるとのことなので、ネクストアクションとしてはこちらを読もうと思います。 あなたのANRはどこから? — 発生する仕組みを診断し、症状別に処方する 発表者: chomi さん(NRIネットコム株式会社) レポート: 岡田 会場が皆うなずいていたセッションだったように思えます。 メインスレッドについての解説を経て、まずは誰しもが経験したことがあるであろう、メインスレッドでの I/O についてのお話から始まりました。その後起動時の重い初期化、ロック競合と進みました。 起動時の重い処理については、特にレガシーコードを触ったことがある人なら対応したことがあるのではないかと思います。本当に Application で初期化すべきかを考えるというのは ANR 以外にも、パフォーマンスの観点から非常に重要です。例として FirebaseSDK の初期化について出ましたが、こちら誰しもがなんとかならないかなと調べたことがあると勝手に思っているので面白かったです。また固有端末依存や Binder 経由の呼び出し先での ANR などについても話があり、やはり皆さん困っているのだなと共感しました。 何より構成と見せ方が完璧だったと思います。スライドは要点だけが目に入る作りで、定義や例などもとても丁寧でしたので、スッと内容が入ってきました。終章の ANR 診断フローチャートについても綺麗にまとめられており、参考になりました。 発表での再現には、公開されているサンプルアプリ DorodoroTimer を用いたそうです。デモモードをONにすると上記の ANR が実際に発生し、コード内の [ANR-xx] マーカーから問題箇所と修正版を見比べられます。 AndroidにおけるServer-Sent Events: 工場の現場を生き抜くリアルタイムストリーム 発表者: Mr. Jasveen Sandral (Industrial Android, Toyota Group Japan) レポート: 鈴木 ( @0muji4_eng ) 本講演は、AndroidにおけるServer-Sent Events(SSE)を用いたリアルタイムストリーミング実装の課題と、その具体的な解決策について論じています。 Webブラウザとは異なり、Androidの標準的なライブラリ(OkHttpなど)にはSSEの自動再接続や状態管理の機能が不足しており、通信障害時にエラーを検知できず画面のデータがフリーズしてしまうエラーケースが存在します。講演者はこの事象を "The Trap of Silence" (沈黙の罠) と呼んでいました。この問題を克服するためには、サーバーに依存するのではなく、クライアント側(Android側)で堅牢な自己回復機能を持つ独自の仕組みを設計する必要性が生じます。 具体的には、サーバーからの定期的な通信(ハートビート)を監視してタイムアウトなどの切断を検知する仕組みや、厳密な状態管理(ステートマシン)の実装が不可欠です。あわせて、再接続時には最後に受信したID(Last-Event-ID)をサーバーへ送信することで、通信断絶中のデータ欠落を補完し、安全にストリーミングを再開する必要があります。 また、頻繁な双方向通信に適したWebSocketとの技術的な比較や、端末がオフラインになった際の適切なUI制御にも触れられています。最終的に、一方向のデータ監視システムにおいてSSEを有効に活用するには、サーバー側でのバッファリングといった設計だけでなく、クライアント側がいかにして通信の切断と復帰に耐えうるアーキテクチャを構築できるかが重要であると結論付けています。 WebAssembly in Android Apps 〜 WASMはJNIの夢を見るか 発表者: keiji_ariyama さん (C-LIS CO., LTD.) レポート: 鈴木 ( @0muji4_eng ) 本講演は、Androidアプリ開発において、C++などで書かれた既存のネイティブライブラリ(OpenJPEG など)を、WebAssembly(Wasm)を用いて安全に再利用するためのアーキテクチャ設計について論じています。 背景として、運転免許証やパスポート、マイナンバーカードなどに格納されている顔写真データ(JPEG 2000形式など)を読み込む際、従来のJNI(Java Native Interface)経由の直接実行では、悪意のある細工された画像データによって深刻な脆弱性を突かれ、アプリ全体が危険にさらされるリスクがありました。 この課題に対する実践的な解決策として、講演者は Wasm と Jetpack JavaScript Engine を組み合わせた多層防御(Defense-in-Depth)を提案しています。Wasmによってシステムコールを持たないメモリ隔離環境(第一層)を構築し、さらにJS Engineによってネットワークやローカルファイルへのアクセス権限を持たない別プロセス(第二層)として実行します。これにより、万が一デコーダーの脆弱性を突かれても、被害をサンドボックス内に完全に封じ込め、アプリ本体への影響を防ぐことが可能になります。 また、実装上の大きな障壁となる プロセス間のデータ転送コスト についても詳細な検証が行われています。文字列変換によるデータ受け渡しでは、プラットフォーム側にネイティブ実装が存在する Base64 を使用するのが最もパフォーマンスが高いことが実証されました。しかし現在では、JS Engine バージョン1.1.0で導入された MessagePort API を活用することで、バイナリデータの双方向通信が可能となり、エンコードのオーバーヘッドが劇的に解消されることが解説されています。あわせて、プロセス間通信の1MB容量制限も、RAM 上のファイルディスクリプターを介することで安全に回避できる点が示されています。 結論として、Wasm はメモリコピーが発生する点(ゼロコピー不可)やコードの隠蔽化に向かない点においてJNIとトレードオフの関係にあります。しかし、外部からの信頼できないデータを処理する要件においては、過去の優れたネイティブ資産を極めて安全にモバイル環境へ持ち込むための、非常に有効なベストプラクティスであると位置づけています。 まとめ 今年は例年と違い、 AI 関連のトピックが増加した印象です! ブースでは AI を用いた開発に関してのアンケートが多数見受けられました! セッションでは デバイス操作はAIエージェントの時代へ。mobile-mcpを活用したAndroid UI/E2Eテストの挑戦 や AI に Inclusive UI を書かせよう — Design Rules Skill で Compose UI を作り直す のような AI を開発効率化に用いる内容から、 Google のオープンモデル Gemma を活用した最新の AI 開発のトレンド のような AI 開発そのものについてまで幅広く講演されており、時代の変化を感じました! またブースには本当に多くの方に足を運んでいただき、たくさんの人にエブリーを知っていただけて、とても良い機会でした! これからもデリッシュキッチン、エブリーのことをよろしくお願いいたします! 最後に エブリーでは、ともに働く仲間を募集しています。 テックブログを読んで少しでもエブリーに興味を持っていただけた方は、ぜひ一度カジュアル面談にお越しください! corp.every.tv さらに、 DroidKaigi & iOSDC After Talks Night 2026 を、ゆめみ、フェンリル、Yappli、WealthNavi、セーフィー、エブリーの6社合同で開催いたします!なお、今回はiOSDC Japan 2026のアフターパーティーも兼ねているので、AndroidエンジニアだけでなくiOSエンジニアの方も交えて、プラットフォームの垣根を越えた活発な技術交流や情報交換をお楽しみいただけます。両カンファレンスの熱気をそのままに、各社によるLTセッションや懇親会をご用意しております。 項目 詳細情報 開催日時 2026年10月2日(金) 19:00 ~ 21:00 開催場所 東京都港区三田一丁目4番1号 住友不動産麻布十番ビル 開催形態 オフライン / オンライン コンテンツ 各社のAndroid & iOSに関するセッション / 懇親会 詳細や参加登録につきましては、以下のリンクよりご確認ください。 yumemi.connpass.com 最後までお読みいただき、ありがとうございました!
G-gen の勝島です。Google Workspace Studio を使用して、Google ドライブに追加されたファイルを Gemini Notebook(旧称 NotebookLM)にソースとして自動で追加するフローの作成手順を紹介します。 はじめに 当記事の概要 Google Workspace Studio とは Gemini Notebook とは 作成するフロー 処理の全体像 追加できるソースの種類 注意事項 フロー作成手順 ノートブックの作成 開始条件の指定 ソース追加ステップの設定 動作確認 はじめに 当記事の概要 当記事では、Google Workspace の業務自動化ワークフローツールである Google Workspace Studio を使用して、 Gemini Notebook (旧称 NotebookLM)へのソース追加を自動化するフローを作成します。具体的には、Google ドライブの特定フォルダにファイルが追加されたことをきっかけに、そのファイルを Gemini Notebook にソースとして自動で追加するよう設定します。 これまで Gemini Notebook にソースを追加するには、ユーザーがノートブックを開き、1つずつ手動で登録する必要がありました。Google Workspace Studio には、Gemini Notebook にソースを追加するステップ(以下、ソース追加ステップ)が用意されており、追加作業をフローに組み込めます。当記事では、このソース追加ステップを中心にフローの作成手順を解説します。 参考 : Workspace Studio の開始条件とステップに関するガイド - Workspace Studio ヘルプ Google Workspace Studio とは Google Workspace Studio は、Google Workspace のアプリケーションを連携させて定型業務を自動化できる、Gemini を搭載したノーコードの自動化ツールです。あらかじめ用意された開始条件(トリガー)とステップ(アクション)を組み合わせて「フロー」を作成することで、プログラミングなしに業務を自動化できます。 たとえば「特定のフォルダにファイルが追加されたら、その内容を Gemini で要約して Google Chat に通知する」といった処理を、コードを書かずに実現できます。 参考 : Google Workspace Studio の使用を開始する - Workspace Studio ヘルプ Google Workspace Studio の概要や基本的な使い方は、以下の記事で詳しく解説しています。当記事ではフローの作成手順に絞るため、基礎的な概念は以下を参照してください。 blog.g-gen.co.jp Gemini Notebook とは Gemini Notebook は、ユーザーが追加したソース(情報源)のみに基づいて回答を生成する、Google の AI リサーチツールです。 PDF や Google ドキュメント、Web URL などをソースとしてノートブックに追加すると、それらの内容に基づいた質問への回答や要約、音声概要(Audio Overview)の生成などを行うことができます。AI の回答がソースに基づくため、ハルシネーション(事実と異なる内容の生成)を抑制できる点が特徴です。 Gemini Notebook の詳細については、以下の記事を参照してください。 blog.g-gen.co.jp 作成するフロー 処理の全体像 今回作成するフローは、Google ドライブの特定フォルダにファイルが追加されたことをきっかけに動き出します。フロー全体は、次の2つのステップで構成されます。 ステップ 種類 処理内容 1 開始条件 フォルダへのアイテム追加を検知する 2 アクション 追加されたファイルを Gemini Notebook にソースとして追加する ステップ1でファイルの追加を検知し、ステップ2でそのファイルを Gemini Notebook のソースに追加します。フォルダにファイルを保存するだけで、ノートブックの情報源が自動で拡充される点が、当記事のポイントです。 追加できるソースの種類 ソース追加ステップでは、以下の種類のソースを Gemini Notebook に追加できます。 テキスト Google ドライブファイルへのリンク Web または YouTube のリンク 当記事ではこのうち、Google ドライブファイルへのリンクを使用します。 注意事項 ソースの追加は即時だが、質問できるようになるまで数分かかる ソース自体はすぐに追加されますが、追加した内容がノートブックにインデックス化され、質問に反映されるまでには数分かかります。フローの実行直後にノートブックへ質問しても、追加したばかりのソースの内容が回答に反映されない場合があります。 Gemini Notebook のソース数には上限がある Gemini Notebook に追加できるソース数には、プランごとの上限があります。フローで自動追加する場合は、上限に達しないよう運用を設計してください。各プランの上限については、以下の公式ドキュメントを参照してください。 参考 : ノートブックの新しいソースを追加または検索する - Gemini Notebook ヘルプ フロー作成手順 ノートブックの作成 フローの作成に先立ち、ソースの追加先となる Gemini Notebook のノートブックを1つ用意します。既存のノートブックを使用する場合は、この手順は不要です。 開始条件の指定 開始条件 は、フローが動き出すきっかけとなるイベントです。Google Workspace Studio では、スケジュール実行やメールの受信など複数の開始条件が用意されています。 当フローでは [フォルダにアイテムが追加されたとき] を開始条件に選びます。監視対象のフォルダにファイルが追加されたタイミングで、フローを起動します。 開始条件の選択画面で [フォルダにアイテムが追加されたとき] を選択します。 続いて、監視するフォルダを指定します。[ドライブ] をクリックし、ソースとして追加したいファイルを格納するフォルダを選択します。 これで、対象のフォルダに新しいファイルが追加されるたびに、フローが実行されます。 ソース追加ステップの設定 開始条件の次に、追加されたファイルを Gemini Notebook のソースに追加するステップを設定します。アクションの [ステップの選択] から、[Gemini Notebook にソースを追加します] を選択します。 ステップを追加すると、ソースの追加先や追加する内容を指定するフィールドが表示されます。各フィールドを以下のとおり設定します。 フィールド名 値 追加先のノートブック ソースの追加先となるノートブックを指定 ソースの種類を選択 ドライブ を指定 ファイルまたはリンクを選択 ドライブ をクリックして ステップ 1: フォルダにアイテムが追加されたとき > アイテムへのリンク を指定 以上で、フォルダへのファイル追加を起点に、Gemini Notebook へソースを自動追加するフローが完成です。 動作確認 フローが完成したら、画面左上のフロー名を分かりやすい名前(ここでは「ナレッジソース自動追加」)に変更し、[オンにする] でフローを有効化します。 フローをオンにした状態で、監視対象のフォルダにファイルを追加します。 フローが実行されると、追加したファイルが Gemini Notebook のソースに追加されます。ノートブックを開くと、ソースの一覧に対象のファイルが表示されています。 なお、ソースの一覧に表示された後も、その内容がインデックス化され質問に反映されるまでには数分かかります。追加直後に質問する場合は注意してください。 フローの実行結果は、画面右上のアクティビティから確認できます。 勝島 祐太郎 (記事一覧) クラウドソリューション部 ソリューションアーキテクト課 2025年1月G-genにジョイン!飲食業界からIT業界に転身したエンジニア。 コーヒーが好きです。
私はC#が好きなのですが、デリゲートを使ったことがないと気付きました。 昔やったプロジェクトでデリゲートを見たことはありますし、先輩が使っているのを見たこともあります。しかし自分自身が使ったことはなくて、食わず嫌いしていたかもと思えてきました。 そこでデリゲートをコードを書きながら学ぼうと考えたのが今回の記事です。私が書いて動かしたコードで解説していきます。 デリゲート(delegate)とは # デリゲートとは委譲という意味です。 委譲とはコトバンクによると「権利・権限などを他の人・機関に譲って任せること」となっています。つまり自分がやることを他人に任せることです。例えば上司が部下に仕事を振ることなどはいい例でしょう。 https://kotobank.jp/word/%E5%A7%94%E8%AD%B2-432326 ならばプログラミングにおいてはやることは処理です。処理の実行を他の機能に任せるということになります。 しかしこれだけだとなんのこっちゃか分かりません。そこでもう少し具体的に言うと、処理(メソッド)の参照を代入して、値ではなく処理(メソッド)を渡すことで、他の機能(クラス)から処理(メソッド)を実行できるようにするとなります。 例えばある処理が完了したらコールバック処理を呼び出してもらうときに使えます。 事前準備 # この記事のサンプルコードを動かすにはVisual StudioまたはVisual Studio CodeでC#開発ができるようセットアップを行ってください。Visual Studio Codeでのセットアップはこちらの記事を参照してください。 VS Codeで始める!わかる&できるC#開発環境の構築【2025年版マニュアル】 IDEのセットアップができたら、次はコンソールアプリのプロジェクトを作成してください。この記事のサンプルコードは手軽に動作確認するためにコンソールアプリで作っています。 デリゲートの基本をサンプルコードで解説 # デリゲートの基本的な書き方 # 早速ですがコードを書いて動きを確認していきます。 動かせるサンプルコードを掲載します。 DelegateSample というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSample { // デリゲートを宣言 // デリゲート型のメソッドを宣言しているようなもの delegate void SampleDelegate(string message); // サンプルの実行 public void ExecSample() { // 宣言したデリゲートを型としてメソッドを引数に渡す // 違和感ある書き方だが、メソッド型のインスタンスを作ってメソッドを代入しているようなイメージ SampleDelegate sampleDelegate = new SampleDelegate(Console.WriteLine); // 試しにメッセージを出してみる // Console.WriteLineを代入したので、デリゲートを実行するとConsole.WriteLineが実行される sampleDelegate("テストメッセージです"); // 次は独自に作ったメソッド(コンソールに文字列を出すだけだが)を代入してみる sampleDelegate = TestMessage; sampleDelegate("カレーとサラダ"); Console.WriteLine(); } void TestMessage(string message) { Console.WriteLine($"今夜の夕食は{message}の予定です。"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); DelegateSample delegateSample = new DelegateSample(); delegateSample.ExecSample(); このコードを実行すると、次のようにメッセージが表示されます。 デリゲート初心者には分かりづらいとか違和感があると感じたかもしれません。 デリゲートの宣言は delegate 型のメソッドを定義することです。そして次に宣言したメソッドを型としたインスタンスを作成します。型がメソッドだなんて一瞬意味が分からなく感じますよね。 そして delegate 型のインスタンスにはメソッドを代入できます。メソッドを実行するための仕組みなので、このようになります。メソッドを代入するということ自体が慣れない行為ですよね。 でもここまで見てみると、引数さえ合っていれば、色んなメソッドを代入して実行可能だろうと分かりますね。 複数のメソッドをまとめるマルチキャストデリゲートというものもあります。例えば先ほどの DelegateSample クラスに次のようにコードを追加してみましょう。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSample { // デリゲートを宣言 // デリゲート型のメソッドを宣言しているようなもの delegate void SampleDelegate(string message); // サンプルの実行 public void ExecSample() { // 宣言したデリゲートを型としてメソッドを引数に渡す // 違和感ある書き方だが、メソッド型のインスタンスを作ってメソッドを代入しているようなイメージ SampleDelegate sampleDelegate = new SampleDelegate(Console.WriteLine); // 試しにメッセージを出してみる // Console.WriteLineを代入したので、デリゲートを実行するとConsole.WriteLineが実行される sampleDelegate("テストメッセージです"); // 次は独自に作ったメソッド(コンソールに文字列を出すだけだが)を代入してみる sampleDelegate = TestMessage; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // ここから追加 // 実はメソッドの追加が可能 sampleDelegate += TestMessageLunch; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // 追加済みメソッドの削除も可能 sampleDelegate -= TestMessage; sampleDelegate("カレーとサラダ"); // ここまで } void TestMessage(string message) { Console.WriteLine($"今夜の夕食は{message}の予定です。"); } // 昼食用のメソッドを追加 void TestMessageLunch(string message) { Console.WriteLine($"今夜の昼食は{message}でした。"); } } } Program.cs を実行すると以下のようになります。 このコードにはデリゲートに対してメソッドを加算及び減算しています。 なんと delegate 型のインスタンスには複数のメソッドの追加や追加したメソッドの削除ができるのです。 これをマルチキャストデリゲートと呼びます。 デリゲートの進化の歴史を知る # 実は先ほど書いたデリゲートのやり方は昔ながらのやり方でした。それがもう少し進化し、C# 2.0の時代には匿名メソッドが使えるようになりました。 匿名メソッドのサンプルコードを掲載します。 DelegateSampleAnonymous というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleAnonymous { // デリゲートを宣言 // デリゲート型のメソッドを宣言しているようなもの delegate void SampleDelegate(string message); // サンプルの実行 public void ExecSample() { // 次は独自に作ったメソッド(コンソールに文字列を出すだけだが)を代入してみる // 定義されたメソッドだけでなく、匿名メソッドも代入可能 SampleDelegate sampleDelegate = delegate(string message) { Console.WriteLine($"今夜の夕食は{message}の予定です。"); }; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // 匿名メソッドを追加してみる sampleDelegate += delegate(string message) { Console.WriteLine($"今夜の昼食は{message}でした。"); }; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // ちなみに匿名メソッドで追加すると削除できない // このように一度別のインスタンスに入れてから追加する必要がある SampleDelegate workDelegate = delegate(string message) { Console.WriteLine($"今夜の朝食は{message}でした。"); }; sampleDelegate += workDelegate; sampleDelegate("カレーとサラダ"); Console.WriteLine(); sampleDelegate -= workDelegate; sampleDelegate("カレーとサラダ"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("匿名メソッドのサンプルです"); Console.WriteLine(); DelegateSampleAnonymous delegateSampleAnonymous = new DelegateSampleAnonymous(); delegateSampleAnonymous.ExecSample(); 実行すると以下のようになります。 ここでちょっと厄介なことは、匿名メソッドを使った場合は追加したメソッドの削除ができないことです。どうしても削除したければ、一度インスタンスに入れてから追加する必要があるのです。 上記のサンプルで言うと workDelegate が該当します。 現在はラムダ式を使うのが主流となっています。ラムダ式を使うサンプルコードを掲載します。 DelegateSampleLambda というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleLambda { // デリゲートを宣言 // デリゲート型のメソッドを宣言しているようなもの delegate void SampleDelegate(string message); // サンプルの実行 public void ExecSample() { // 次は独自に作ったメソッド(コンソールに文字列を出すだけだが)を代入してみる // 定義されたメソッドだけでなく、ラムダ式も代入可能 SampleDelegate sampleDelegate = (string message) => { Console.WriteLine($"今夜の夕食は{message}の予定です。"); }; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // ラムダ式を追加してみる sampleDelegate += (string message) => { Console.WriteLine($"今夜の昼食は{message}でした。"); }; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // ちなみにラムダ式で追加すると削除できない // このように一度別のインスタンスに入れてから追加する必要がある SampleDelegate workDelegate = (string message) => { Console.WriteLine($"今夜の朝食は{message}でした。"); }; sampleDelegate += workDelegate; sampleDelegate("カレーとサラダ"); Console.WriteLine(); sampleDelegate -= workDelegate; sampleDelegate("カレーとサラダ"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("ラムダ式のサンプルです"); Console.WriteLine(); DelegateSampleLambda delegateSampleLambda = new DelegateSampleLambda(); delegateSampleLambda.ExecSample(); 実行すると以下のようになります。 ラムダ式になるともはや delegate という記述すらなくなります。一番シンプルな書き方です。 FuncとActionを使えば宣言不要 # 戻り値がない処理ならAction # ここまでのやり方は delegate 型のメソッドを宣言して、そのインスタンスにメソッドを代入するというものでした。わざわざ宣言しているので、用途別にデリゲートを作成する必要がありました。 そこで汎用型として戻り値がない場合は Action 、戻り値がある場合は Func というものが登場しました。 Action を使ったサンプルコードを掲載します。 DelegateSampleAction というクラスを作り、以下のコードを書いてください。 このようにジェネリックで引数の型を指定します。わざわざ delegate 型のメソッドを宣言する必要がないので、コードもシンプルになります。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleAction { public void ExecSample() { // Actionデリゲートを使用するサンプル // Actionデリゲートは戻り値がvoidで、引数の型を指定できる Action<string> actionDelegate = (message) => { Console.WriteLine($"今夜の夕食は{message}の予定です。"); }; actionDelegate("カレーとサラダ"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("Actionのサンプルです"); Console.WriteLine(); DelegateSampleAction delegateSampleAction = new DelegateSampleAction(); delegateSampleAction.ExecSample(); 実行すると以下のようになります。 ちなみにメソッドの追加や削除については delegate を使う場合と同様に+や-などの演算子を使ってください。また匿名メソッドやラムダ式では削除ができないことも同様ですので、削除したいときは一度インスタンスに入れてから追加してください。 戻り値がある処理ならFunc # Func を使ったサンプルコードを掲載します。 DelegateSampleFunc というクラスを作り、以下のコードを書いてください。ジェネリックの型指定で、<引数の型, 戻り値の型>というように指定します。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleFunc { public void ExecSample() { // Actionデリゲートを使用するサンプル // Actionデリゲートは戻り値がvoidで、引数の型を指定できる Func<int, string> funcDelegate = (num) => { return $"入力された値は{num}です。"; }; Console.WriteLine(funcDelegate(5)); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("Funcのサンプルです"); Console.WriteLine(); DelegateSampleFunc delegateSampleFunc = new DelegateSampleFunc(); delegateSampleFunc.ExecSample(); 実行すると以下のようになります。 Func もメソッドの追加や削除については delegate を使う場合と同様です。 デリゲートの本当の使い道 # コールバック処理 # コールバック処理のサンプルコードを掲載します。 DelegateSampleAction というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class CallbackSample { // コールバックのサンプル // ここでは、処理が終わった後に呼び出されるメソッドをデリゲートとして渡す // デリゲートを使うことで、処理が終わった後に呼び出されるメソッドを自由に変更できる public void ExecSample(string message, Action<string> callback) { Console.WriteLine($"処理中: {message}"); // 処理が終わった後にコールバックを呼び出す callback?.Invoke($"処理が完了しました: {message}"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("コールバックのサンプルです"); Console.WriteLine(); CallbackSample callbackSample = new CallbackSample(); callbackSample.ExecSample("カレーとサラダ", (result) => { Console.WriteLine(result); }); 実行すると以下のようになります。 このようにメソッドの引数に Func や Action を定義することで処理を受け取ります。そして Invoke メソッドで処理を実行します。 条件に応じた処理の差し替え # 実はLINQの Where は引数が Func になっています。つまり Where メソッドの中ではデリゲートを使っているのです。 では Func の内容次第で結果が異なる例を作ってみましょう。 DelegateSampleLinq というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleLinq { public void ExecSample() { List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // まずは偶数だけを抽出してみる var evenNumbers = FilterNumbers(numbers, (num) => num % 2 == 0); Console.WriteLine("偶数のリスト:"); Console.WriteLine(evenNumbers.Count > 0 ? string.Join(", ", evenNumbers) : "該当する値はありません。"); // 続いて奇数を抽出してみる var oddNumbers = FilterNumbers(numbers, (num) => num % 2 != 0); Console.WriteLine("奇数のリスト:"); Console.WriteLine(oddNumbers.Count > 0 ? string.Join(", ", oddNumbers) : "該当する値はありません。"); } /// <summary> /// 引数として渡されたFuncを実行し、条件に一致する値だけを返す /// </summary> /// <param name="numbers"></param> /// <param name="predicate"></param> /// <returns></returns> static List<int> FilterNumbers(List<int> numbers, Func<int, bool> predicate) { List<int> resultList = new List<int>(); foreach (var number in numbers) { if (predicate(number)) { resultList.Add(number); } } return resultList; } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("条件を切り替えるサンプルです"); Console.WriteLine(); DelegateSampleLinq delegateSampleLinq = new DelegateSampleLinq(); delegateSampleLinq.ExecSample(); 実行すると以下のようになります。 デリゲートの脆弱性を補うためのevent # ここまでデリゲートの使い方について色々と見てきました。 しかし便利な反面、危険も伴います。何せ代入次第で何とでも処理内容を変えられます。また Invoke メソッドによって好きなところで実行が可能です。脆弱な面があることは否めません。 下手すると不具合の元になるのはもちろん、セキュリティ面での脆弱性も怖いところです。 そこで event というキーワードを付けることで、外部クラスからの代入や実行ができなくなります。 event を使ったサンプルコードを掲載します。 DelegateSampleEvent というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleEvent { // パブリックだから他のクラスからアクセス可能 public event Func<int, string> SampleEvent; } internal class DelegateSampleExternal { public void ExecSample() { DelegateSampleEvent sample = new DelegateSampleEvent(); // 外部クラスからのデリゲートの代入は不可 sample.SampleEvent = (int x) => $"入力された値は{x}です。"; // 外部クラスからのデリゲートの追加と削除は可能 sample.SampleEvent += (int x) => $"入力された値は{x}です。"; Func<int, string> func = (int x) => $"今日はコーヒーを{x}杯飲みました。"; sample.SampleEvent += func; sample.SampleEvent -= func; // 外部クラスからの実行は不可 sample.SampleEvent.Invoke(99); } } } 上記のようにコードを書くと、以下のスクリーンショットのように代入と Invoke メソッドのところでコンパイルエラーが発生します。 ひとまず先ほどのコードでコンパイルエラーが出ている個所はコメントアウトしてください。 フードコートでの注文を例にデリゲートを試す # デリゲートのやり方が分かったところで、現実世界をイメージして練習してみたいと思います。そこでフードコードのスイーツ屋をテーマに扱います。ただ文字の表示や計算をするよりは楽しい内容になるし、委譲をイメージしやすいと考えたためです。 業務フローは以下の図のようになります。 ここではコードをかなり端折って解説します。本当は注文内容だって明細を持てるようにしたり、注文内容も動的に変えられるようにしたりする必要がありますが、今回はそこが主題でないため簡易的なメッセージで済ませます。 それではソースコードを掲載します。 Models というフォルダに作って、その中に以下の5つのクラスを作成してください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Buzzer { public int No { get; set; } public Buzzer(int no) { No = no; } public void Ring() { Console.WriteLine($"ピー!ピー!ピー!{No}番のブザーが鳴っています!"); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Cook { public void CookOrder(Order order) { Console.WriteLine($"料理人が注文を受け取りました: {order.OrderContent}"); Console.WriteLine("料理を作っています..."); Console.WriteLine("料理が完成しました!"); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Customer { private Buzzer Buzzer { get; set; } public Order MakeOrder() { Console.WriteLine("お客様が注文しました。"); return new Order("パンケーキとコーヒーのセット"); } public void Pay() { Console.WriteLine("お会計を済ませました。"); } public void PickUpBuzzer(Buzzer buzzer) { Buzzer = buzzer; Console.WriteLine($"お客様が{buzzer.No}番のブザーを受け取りました。"); } public void PickUpFood() { Console.WriteLine($"お客様が{Buzzer.No}番のブザーを渡しました。"); Console.WriteLine("お客様が料理を受け取りました。"); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Order { public string? OrderContent { get; set; } public Order(string? orderContent) { OrderContent = orderContent; } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Staff { private delegate void BuzzerRingHandler(); private IDictionary<int, BuzzerRingHandler> _buzzerHandlers = new Dictionary<int, BuzzerRingHandler>(); public void TakeOrder(Order order) { Console.WriteLine($"スタッフが注文を受け取りました: {order.OrderContent}"); Console.WriteLine("お会計は1,500円になります。"); } public Buzzer HandOverBuzzer(int no) { Buzzer buzzer = new Buzzer(no); Console.WriteLine($"スタッフが{no}番のブザーを渡しました。"); _buzzerHandlers.Add(no, buzzer.Ring); return buzzer; } public void RingBuzzer(int no) { if (_buzzerHandlers.TryGetValue(no, out var buzzer)) { buzzer.Invoke(); _buzzerHandlers.Remove(no); } } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("フードコートで料理が出来たらブザーを鳴らすサンプルです"); Console.WriteLine(); Customer customer = new Customer(); Staff staff = new Staff(); Cook cook = new Cook(); Order order = customer.MakeOrder(); // 注文とお会計 staff.TakeOrder(order); customer.Pay(); // ブザーを渡す int no = 1; Buzzer buzzer = staff.HandOverBuzzer(no); customer.PickUpBuzzer(buzzer); // 調理 cook.CookOrder(order); // ブザーを鳴らして料理を渡す staff.RingBuzzer(no); customer.PickUpFood(); 実行すると以下のようになります。 ポイントとしては店員がどのお客さんに何番のブザーを渡したかを管理しているということです。 プログラムとしては Staff クラスが Dictionary で番号とブザーを鳴らすメソッドの組み合わせを持っています。 こうすることで、料理ができたときに店員が顧客の手元にあるブザーを鳴らせるようにできます。 おわりに # コードを書いて動かしてみることで、デリゲートとは何かが少し分かりました。やっぱり書いて動かしてみるのが一番ですね。 デリゲートが分からないという方はこの記事のフードコートみたいなものを作ってみてください。

動画

書籍