C# Delegateを理解する
C# の Delegate は強力な機能ですが、多くの開発者はその効果的な使い方に精通していません。 Tim Corey の"Delegates in C# - A practical demonstration, including Action and Func"というビデオでは、デリゲートとは何か、どのように使うのか、なぜ便利なのかを徹底的に説明しています。
この記事では、C#のデリゲートに関するTimの専門的な洞察を提供し、その使用方法と実用的なアプリケーションの明確な説明を提供します。 ショッピングカートシステムでの使用例などを交えながら、デリゲートがコードの柔軟性と効率性をどのように高めることができるかを学びます。
はじめに
Timはデリゲートの概念を紹介し、C#におけるデリゲートのパワーと汎用性を強調します。 彼は、威圧的な専門用語がいくつかあるにもかかわらず、デリゲートの基礎はシンプルであると視聴者に保証する。 Timは、デリゲートを解明し、funcやactionのような特殊な型をカバーすることを目指しています。
デモ アプリケーション ウォークスルー
Timは、デリゲートの使い方を説明するためにデモアプリケーションをセットアップします。 このソリューションには、コンソールUI、デモライブラリ、WinForm UIの3つのプロジェクトが含まれています。 最初は、コンソールUIとデモライブラリに焦点を当てます。
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleUI
{
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal():C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
}
public class ShoppingCartModel
{
public List<ProductModel> Items { get; set; } = new List<ProductModel>();
public decimal GenerateTotal()
{
decimal subtotal = Items.Sum(x => x.Price);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
}
public class ProductModel
{
public string ItemName { get; set; }
public decimal Price { get; set; }
}using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleUI
{
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal():C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
}
public class ShoppingCartModel
{
public List<ProductModel> Items { get; set; } = new List<ProductModel>();
public decimal GenerateTotal()
{
decimal subtotal = Items.Sum(x => x.Price);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
}
public class ProductModel
{
public string ItemName { get; set; }
public decimal Price { get; set; }
}Timは、デモアプリケーションの構造と機能を説明します:
ショッピングカートモデル:アイテム (ProductModel) のリストでショッピングカートを表現し、小計に基づく割引で合計金額を計算します。
商品モデル:名前と価格のプロパティを持つ個々のアイテムを表します。
- コンソールアプリケーション:カートにデモデータを入力し、合計を計算し、表示します。
割引の理解
Timは、GenerateTotalメソッドの割引ロジックを説明し、どのように小計が適用される割引を決定するかを説明します:
- 100ドル以上の小計は20%オフ。
- 50ドル以上の小計は15%オフ。
- 10ドル以上の小計は10%オフ。
- 小計10ドル以下の割引はありません。
ティムは、計算と割引のロジックを示すためにブレークポイントを使用し、視聴者がデリゲートを導入する前に基礎を理解していることを確認します。
デリゲートの説明と作成
このセクションでは、Tim CoreyがC#におけるデリゲートの概念に飛び込み、デリゲートがどのように機能するかを説明し、実用的なコード例でその使い方を実演します。
デリゲートとは何ですか?
Timは、デリゲートは基本的にメソッドをパラメータとして渡す方法であると説明します。 変数やプロパティを渡す代わりにメソッドを渡すことで、より柔軟で再利用可能なコードを実現します。
デリゲートの作成と使用
ここでは、Timがデリゲートを作成し使用するプロセスをどのように説明しているかを紹介します:
1.デリゲートを定義する:
* デリゲートはクラスの先頭で定義され、戻り値の型とパラメータの型を指定します。
```csharp
public delegate void MentionDiscount(decimal subtotal);
```
* このデリゲートは void を返し、パラメータとして小数を取るメソッドを指定します。2.メソッドでデリゲートを使う:
* デリゲートは ShoppingCartModel クラスの GenerateTotal メソッドのパラメータとして使用されます。
```csharp
public decimal GenerateTotal(MentionDiscount mentionDiscount)
{
decimal subtotal = Items.Sum(x => x.Price);
// Call a method passed as a delegate
mentionDiscount(subtotal);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
```3.デリゲートに渡すメソッドを作成する:
* デリゲートのシグネチャに一致するメソッドが Program クラスに作成されます。
```csharp
private static void SubtotalAlert(decimal subtotal)
{
Console.WriteLine($"The subtotal is {subtotal:C2}");
}
```4.GenerateTotalメソッドの呼び出し:
* このメソッドは、デリゲートを介して GenerateTotal メソッドに渡されます。
```csharp
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(SubtotalAlert):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
```アプリケーションの実行
Timは、デリゲートがどのように動作するかを示すためにアプリケーションを実行します。 コンソール出力は、カートの小計と合計を示し、SubtotalAlert メソッドがデリゲート経由で正常に渡され実行されたことを示しています。

FuncとAction:デリゲートで解決できる問題
ティム・コーリーは次に、C#におけるfuncとactionのデリゲートの使い方を説明します。 これは、ジェネリックスのデリゲート使用を簡素化するためにMicrosoftによって提供された特別なタイプのデリゲートです。
問題の特定
Timは、よくある問題である、GenerateTotalメソッドにハードコードされた割引ロジックに注目しています。 このアプローチは柔軟性に欠け、割引ルールが変更されるたびにコードの変更、再コンパイル、再デプロイメントが必要になります。
public decimal GenerateTotal()
{
decimal subtotal = Items.Sum(x => x.Price);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}public decimal GenerateTotal()
{
decimal subtotal = Items.Sum(x => x.Price);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}Func Delegate の紹介
Timは、ハードコードされた割引の問題に対処するためにfuncデリゲートを紹介します。 funcデリゲートは、戻り値の型と最大16個の入力パラメータを持つメソッドシグネチャを表す一般的なデリゲートです。
1.Funcデリゲートの定義:
* funcデリゲートは、割引計算を動的に処理するためにGenerateTotalメソッドで使用されます。
```csharp
public decimal GenerateTotal(Func<List<ProductModel>, decimal, decimal> calculateDiscountedTotal)
{
decimal subtotal = Items.Sum(x => x.Price);
MentionDiscount(subtotal);
return calculateDiscountedTotal(Items, subtotal);
}
```2.割引計算方法の作成:
* funcデリゲートのシグネチャに一致するメソッドがProgramクラスに作成されます。
```csharp
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
```3.メソッドを Func Delegate に渡す:
* CalculateLevelDiscount メソッドは、func デリゲートを介して GenerateTotal メソッドに渡されます。
```csharp
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(CalculateLevelDiscount):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
}
```アプリケーションの実行
Timは、修正したアプリケーションを実演し、それが正しく機能し、提供されたロジックに基づいて動的に割引を計算することを示します。

デリゲートと関数の違い
Tim は、カスタムデリゲートと func デリゲートを比較しています:
委任:シグネチャの明確な定義を必要とし、明確な文書と構造を提供します。
- Func:より簡潔ですが、入力と出力の型を毎回指定する必要があり、明確でない場合があります。
どちらのアプローチも柔軟性がありますが、特定のユースケースとアプリケーションの複雑さによって選択します。
すべての作業が別の場所で行われるのであれば、なぜデリゲートを使用するのですか?
ティム・コリーは、デリゲートの使用に関するよくある質問に答えています:すべての作業が他の場所で行われているように見えるのに、なぜデリゲートを持つのですか?
ティムは、デリゲートの目的はコードに柔軟性と拡張性を持たせることだと説明する。 ShoppingCartModel クラスの GenerateTotal メソッドは、単に割引を計算するだけではありません。 また、在庫の有無の確認、カートの内容の検証、その他のビジネスロジックなどのタスクを処理することもあります。 デリゲートを使用すると、コアメソッドを変更することなく、独自のタスクやカスタム動作のために特定のメソッドを渡すことができます。 これにより、コードがよりモジュール化され、保守が容易になります。
デリゲートは、以下のような場面で特に役立ちます:
- 異なるビジネスルールやロジックを動的に適用する。
- コアとなるメソッドは汎用的で再利用可能なものにしてください。
- コア・メソッドを修正することなく、特定のケースにカスタム動作を実装する。
アクション委任:作成と説明
Timは、C#のデリゲートのもう一つの特別なタイプであるActionデリゲートを紹介します。 ActionデリゲートはFuncに似ていますが、値を返しません(つまりvoidを返します)。
1.アクションデリゲートの作成:
* GenerateTotalメソッドでActionデリゲートを定義し、アラートやメッセージを処理します。
```csharp
public decimal GenerateTotal(Func<List<ProductModel>, decimal, decimal> calculateDiscountedTotal, Action<string> tellUserWeAreDiscounting)
{
decimal subtotal = Items.Sum(x => x.Price);
MentionSubtotal(subtotal);
tellUserWeAreDiscounting("We are applying your discount.");
return calculateDiscountedTotal(Items, subtotal);
}
```2.アラートメソッドを作成する:
* Actionデリゲートのシグネチャにマッチするメソッドを定義してください。
```csharp
private static void AlertUser(string message)
{
Console.WriteLine(message);
}
```3.アクション・デレゲートにメソッドを渡す:
* AlertUserメソッドをActionデリゲートを介してGenerateTotalメソッドに渡します。
```csharp
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(CalculateLevelDiscount, AlertUser):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
private static void AlertUser(string message)
{
Console.WriteLine(message);
}
}
```匿名メソッドを作成する:匿名デリゲート
Timは、名前を付けずにその場でメソッドを定義できる無名メソッドの使い方を紹介します。
1.匿名メソッドの定義:
* 名前付きメソッドを作成する代わりに、必要な場所で直接メソッドを定義することができます。
```csharp
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal((items, subtotal) =>
{
if (subtotal > 100) return subtotal * 0.80M;
else if (subtotal > 50) return subtotal * 0.85M;
else if (subtotal > 10) return subtotal * 0.90M;
else return subtotal;
},
(message) => Console.WriteLine(message)):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
```2.構文を理解する:
* 非同期メソッド構文は`=>`演算子(ラムダ式)を使用して、メソッド本体を直接インラインで定義します。
* 戻り値の型やメソッド名を指定する必要はありません。Func、Action、匿名メソッドを含むデリゲートを使用することで、開発者はより動的でモジュール化されたコードを作成することができ、柔軟で再利用可能なコンポーネントを作成することができます。
他のプロジェクトでデリゲートを使用する:WinForms
このセグメントでは、Tim CoreyがWinFormsアプリケーションにデリゲートの使用を拡張することで、デリゲートのパワーを実証します。 これは、デリゲートがさまざまなユーザーインターフェイス(UI)のコンテキストでどのように異なる動作を促進できるかを強調するものです。
WinFormsアプリケーションのセットアップ
1.2つのボタンを持つWinForm UI:
* フォームには2つのボタンがあり、1つはメッセージボックスのデモ用、もう1つはテキストボックスのデモ用です。
* ShoppingCartModel と、それにデモデータを入力するメソッドも含まれています。public partial class Dashboard : Form
{
ShoppingCartModel cart = new ShoppingCartModel();
public Dashboard()
{
InitializeComponent();
PopulateCartWithDemoData();
}
private void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private void messageBoxDemoButton_Click(object sender, EventArgs e)
{
decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
MessageBox.Show($"The total is {total:C2}");
}
private void textBoxDemoButton_Click(object sender, EventArgs e)
{
// Code for TextBox demo will go here
}
}public partial class Dashboard : Form
{
ShoppingCartModel cart = new ShoppingCartModel();
public Dashboard()
{
InitializeComponent();
PopulateCartWithDemoData();
}
private void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private void messageBoxDemoButton_Click(object sender, EventArgs e)
{
decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
MessageBox.Show($"The total is {total:C2}");
}
private void textBoxDemoButton_Click(object sender, EventArgs e)
{
// Code for TextBox demo will go here
}
}デリゲートのメソッドを作成する
1.PrintOutDiscountAlert:
* この方法は、割引情報のアラートを表示するために使用されます。
```csharp
private void PrintOutDiscountAlert(string message)
{
MessageBox.Show(message);
}
```2.SubtotalAlert:
* この方法では、小計がメッセージボックスに表示されます。
```csharp
private void SubtotalAlert(decimal subtotal)
{
MessageBox.Show($"The subtotal is {subtotal:C2}");
}
```3.CalculateLevelDiscount:
* この方法では、カート内の商品数に基づいて割引が計算されます。
```csharp
private decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (items.Count > 3)
{
return subtotal - 3M;
}
return subtotal - items.Count;
}
```WinFormsとデリゲートを統合する
1.ボタンのクリックイベントでデリゲートを使う:
* `messageBoxDemoButton_Click`メソッドは、ジェネレートトータルメソッドにデリゲートを渡し、メッセージボックスを使って結果を処理する方法を示します。
```csharp
private void messageBoxDemoButton_Click(object sender, EventArgs e)
{
decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
MessageBox.Show($"The total is {total:C2}");
}
```2.アプリケーションの実行:
* ボタンがクリックされると、WinFormsアプリケーションはメッセージボックスを使って小計と合計を表示し、デリゲートの柔軟性を示します。
結論
Tim Coreyは、C#におけるデリゲートについて、その基本、高度な使用法、ショッピングカートでのデリゲートの使用などの実用的な例を取り上げながら、わかりやすく説明しています。 Func、Action、匿名メソッドなど、デリゲートが柔軟で再利用可能なコードをどのように実現するかを示している。 ビデオの全文をご覧になり、プロジェクトでデリゲートを効果的に適用する方法を学んでください!

