了解 C# 委托
C# 中的委托是一项强大的功能,但许多开发人员并不熟悉如何有效地使用它们。 Tim Corey 的视频" C# 中的委托 - 实际演示,包括 Action 和 Func "对委托是什么、如何使用它们以及为什么它们有用进行了详尽的解释。
本文将为您提供 Tim 关于 C# 中委托的专家见解,清晰地解释其用法和实际应用。 您将学习委托如何增强代码的灵活性和效率,例如在购物车系统中使用委托的示例。
简介
Tim 介绍了委托的概念,强调了委托在 C# 中的强大功能和多功能性。 他向观众保证,尽管有些术语令人望而生畏,但代表制的基本原理很简单。 Tim 旨在揭开委托的神秘面纱,并介绍 func 和 action 等特殊类型。
应用程序演示
Tim 设置了一个演示应用程序来说明委托的用法。 该解决方案包含三个项目:控制台 UI、演示库和 WinForm 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 使用断点来演示计算和折扣逻辑,确保观众在向代表介绍之前理解基础知识。
解释和创建代表
在本节中,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 方法已通过委托成功传递和执行。

函数和动作:你可以用委托解决的问题
Tim Corey 随后探讨了 C# 中 func 和 action delegate 的使用。 这些是微软提供的特殊类型的委托,旨在简化泛型委托的使用。
识别问题
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 委托
Tim 引入了 func delegate 来解决硬编码折扣问题。 func 委托是一个通用委托,它表示一个方法签名,带有返回类型和最多 16 个输入参数。
1.定义函数委托:
* 函数委托在 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.创建折扣计算方法:
* 在 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.将方法传递给函数委托:
* CalculateLevelDiscount 方法通过函数委托传递给 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 对比了自定义委托和函数委托:
*委托:需要明确定义签名,提供清晰的文档和结构。
*函数:更简洁,但每次都需要指定输入和输出类型,这可能不太清晰。
两种方法都提供了灵活性,但选择取决于具体的使用案例和应用程序的复杂程度。
既然所有工作都在其他地方完成,为什么还要使用委托呢?
Tim Corey 回答了一个关于使用委托的常见问题:如果所有工作似乎都在其他地方完成,为什么还要使用委托呢?
Tim解释说,委托的目的是为代码提供灵活性和可扩展性。 ShoppingCartModel 类中的 GenerateTotal 方法可能不仅仅用于计算折扣。 它还可以处理诸如检查库存可用性、验证购物车内容或其他业务逻辑等任务。 委托允许您传递特定方法以执行特殊任务或自定义行为,而无需更改核心方法。 这使得代码更具模块化,更易于维护。
在以下情况下,委托尤其有用:
- 动态应用不同的业务规则或逻辑。 保持核心方法的通用性和可重用性。
- 在不修改核心方法的情况下,针对特定情况实现自定义行为。
行动代表:创建和解释
Tim 介绍了 Action 委托,这是 C# 中的另一种特殊类型的委托。 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.将方法传递给操作委托:
* 通过 Action 委托将 AlertUser 方法传递给 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.理解语法:
* 匿名方法语法使用`=>`运算符(lambda表达式)直接内联定义方法体。
* 无需指定返回类型或方法名称。通过使用委托(包括 Func、Action 和匿名方法),开发人员可以创建更动态、模块化的代码,从而实现灵活且可重用的组件。
在其他项目中使用委托:WinForms
在本部分中,Tim Corey 通过将委托的使用扩展到 WinForms 应用程序来演示委托的强大功能。 这突显了委托如何在各种用户界面 (UI) 环境中促进不同的行为。
设置 WinForms 应用程序
1.带有两个按钮的 WinForm 用户界面:
* 该表单有两个按钮:一个用于演示消息框,一个用于演示文本框。
* 此外,还包括 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
}
}创建委托方法
PrintOutDiscountAlert :
- 此方法将用于显示包含折扣信息的提醒。
private void PrintOutDiscountAlert(string message) { MessageBox.Show(message); }private void PrintOutDiscountAlert(string message) { MessageBox.Show(message); }
2.小计提醒:
* 此方法会在消息框中显示小计。
```csharp
private void SubtotalAlert(decimal subtotal)
{
MessageBox.Show($"The subtotal is {subtotal:C2}");
}
```3.计算等级折扣:
* 此方法将根据购物车中的商品数量计算折扣。
```csharp
private decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (items.Count > 3)
{
return subtotal - 3M;
}
return subtotal - items.Count;
}
```将委托与 WinForms 集成
1.在按钮点击事件中使用委托:
* `messageBoxDemoButton_Click`方法展示了如何将委托传递给GenerateTotal方法,并使用消息框处理结果。
```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 和匿名方法。 观看完整视频,了解如何在项目中有效地运用委托!

